Это немного запутанно для меня, чтобы следовать, что бы ни думали Oxbow Lakes.
Прежде всего, я бы хотел уточнить один момент: нет условий перерывов в понимании. Они не являются циклами, такими как C (или Java) for
.
Что означает if
для понимания, это охранник. Например, скажем, я делаю это:
for {i <- 1 to 10
j <- 1 to 10
if i != j
} yield (i, j)
Цикл не "останавливается", когда условие ложно. Он просто пропускает итераций, для которых это условие ложно, и переходит к истинным. Вот еще один пример:
for {i <- 1 to 10
j <- 1 to 10
if i % 2 != 0
} yield (i, j)
Вы сказали, что у вас нет побочных эффектов, поэтому я могу пропустить целую главу о побочных эффектах и мерах предосторожности для понимания. С другой стороны, чтение сообщения в блоге, которое я недавно написал на Strict Ranges - неплохая идея.
Так что ... сдавайся на перерывах. Их можно заставить работать, но они не работают. Попробуйте перефразировать проблему более функциональным способом, и необходимость условия прерывания будет заменена чем-то другим.
Далее, Oxbow верен в том, что (s -> (p,c) =>
не разрешен, потому что не определен экстрактор для объекта с именем ->
, но, увы, даже (a :: b) =>
не будет разрешен, потому что нет сопоставления с образцом происходит в функциональном объявлении литерального параметра. Вы должны просто указать параметры в левой части =>
, не выполняя разложения. Вы можете, однако, сделать это:
if ( extension.exists( t => val (s, (p,c)) = t; this.isGoal( s ) ) )
Обратите внимание, что я заменил ->
на ,
. Это работает, потому что a -> b
является синтаксическим сахаром для (a, b)
, который сам по себе является синтаксическим сахаром для Tuple2(a, b)
. Поскольку вы не используете ни p
, ни c
, это тоже работает:
if ( extension.exists( t => val (s, _) = t; this.isGoal( s ) ) )
Наконец, ваш рекурсивный код в порядке, хотя, вероятно, не оптимизирован для хвостовой рекурсии. Для этого вы либо делаете свой метод final
, либо делаете рекурсивную функцию приватной для метода. Как это:
final def breadthFirstHelper
или
def breadthFirstHelper(...) {
def myRecursiveBreadthFirstHelper(...) { ... }
myRecursiveBreadthFirstHelper(...)
}
В Scala 2.8 есть аннотация под названием @TailRec
, которая сообщит вам, можно ли сделать функцию хвостовой рекурсивной или нет. И, на самом деле, кажется, будет флаг для отображения предупреждений о функциях, которые могут сделать хвосто-рекурсивными, если их немного изменить, например, как указано выше.
EDIT
Относительно решения Oxbow, использующего case
, это литерал функции или частичной функции. Его тип будет зависеть от того, что требует логический вывод. В этом случае, потому что это то, что exists
принимает, функцию. Однако нужно быть внимательным, чтобы всегда было совпадение, иначе вы получите исключение. Например:
scala> List(1, 'c') exists { case _: Int => true }
res0: Boolean = true
scala> List(1, 'c') exists { case _: String => true }
scala.MatchError: 1
at $anonfun$1.apply(<console>:5)
... (stack trace elided)
scala> List(1, 'c') exists { case _: String => true; case _ => false }
res3: Boolean = false
scala> ({ case _: Int => true } : PartialFunction[AnyRef,Boolean])
res5: PartialFunction[AnyRef,Boolean] = <function1>
scala> ({ case _: Int => true } : Function1[Int, Boolean])
res6: (Int) => Boolean = <function1>
РЕДАКТИРОВАТЬ 2
Решение, предлагаемое Oxbow, использует сопоставление с образцом, потому что оно основано на функциональных литералах, использующих операторы case
, которые используют сопоставление с образцом. Когда я сказал, что это невозможно, я говорил о синтаксисе x => s
.