2017-02-14 10 views
1

Я хочу, чтобы применить функцию f к каждому элементу List и не останавливаться на первой ошибки, но бросить последнюю ошибку (если таковые имеются) только:Scala хвостовая рекурсия от наконец, блок

@annotation.tailrec 
def tryAll[A](xs: List[A])(f: A => Unit): Unit = { 
    xs match { 
    case x :: xt => 
     try { 
     f(x) 
     } finally { 
     tryAll(xt)(f) 
     } 
    case _ => 
    } 
} 

Но, то выше код не компилируется - он жалуется, что эта функция не является хвостовой рекурсивной. Почему нет?

+1

Функция не является хвостовой рекурсивной, поскольку в случае, когда исключение получает бросок, блок 'finally' не является последним кодом для выполнения. –

+0

@HristoIliev: Я вижу - как я могу написать это эффективным и идиоматическим образом? – pathikrit

+0

Я считаю, что идиоматическим способом было бы использовать 'scala.util.Try', чтобы обернуть вызовы функций, но я не могу предоставить вам пример кода. –

ответ

1

Это решение перебирает все элементы и производит (броски) последней ошибки, если таковые имеются:

def tryAll[A](xs: List[A])(f: A => Unit): Unit = { 
    val res = xs.foldLeft(Option.empty[Throwable]) { 
    case (maybeThrowable, a) => 
     Try(f(a)) match { 
     case Success(_) => maybeThrowable 
     case Failure(e) => Option(e) 
     } 
    } 

    res.foreach(throwable => throw throwable) 
} 
-1

Не уверен, что намерение метода, но вы можете что-то подобное:

final def tryAll[A](xs: List[A])(f: A => Unit): Unit = { 
     xs match { 
     case x :: xt => 
      try { 
      f(x) 
      } catch { 
      case e => tryAll(xt)(f) 
      } 
     case _ => //do something else 
     } 
    } 
+0

приведет к тому, что StackOverflow будет иметь список> 1M. – pathikrit

-1

Я знаю, что этот способ использовать @ annotation.tailrec

Отсюда:

def fac(n:Int):Int = if (n<=1) 1 else n*fac(n-1) 

Вы должны иметь это:

@scala.annotation.tailrec 
def facIter(f:Int, n:Int):Int = if (n<2) f else facIter(n*f, n-1) 
def fac(n:Int) = facIter(1,n) 
+0

Вы должны накапливать значения в f, поэтому, когда вы достигнете базового аргумента, верните его. Это не имеет смысла для вашего метода: / – mychemicalro

0

Как упомянуто @HristoIliev, ваш метод не может быть хвостовой рекурсией, поскольку finally вызов не гарантируется хвост вызова. Это означает, что любой метод, использующий try таким образом, не будет хвостом рекурсивным. См. Также this answer.

Вызов метода снова - это странный способ многократно повторять попытку, пока он не преуспеет, потому что на каждом этапе он бросает исключение, которое вы, по-видимому, не обрабатываете. Вместо этого я бы сказал, используя функциональный подход с Try, принимая неудачи с точки зрения, пока операция не завершится успешно. Единственным недостатком этого подхода является то, что он не бросает никаких исключений для вас, чтобы вы могли справиться на этом пути (что также может быть преимуществом!).

def tryAll[A](xs: List[A])(f: A => Unit): Unit = 
    xs.view.map(x => Try(f(x))).takeWhile(_.isFailure).force 


scala> val list = List(0, 0, 0, 4, 5, 0) 

scala> tryAll(list)(a => println(10/a)) 
2 

Если вы действительно хотите обрабатывать исключения (или только последнее исключение), вы можете изменить тип возвращаемого tryAll к List[Try[Unit]] (или просто Try[Unit], если изменить код только взять последний). Для возвращаемого типа метода лучше описать часть того, что он на самом деле делает - потенциально возвращающие ошибки.