2015-04-30 3 views
0

Теперь у меня есть некоторые Scala код, подобный следующему:Раннее возвращение из для цикла в Scala

def foo(x: Int, fn: (Int, Int) => Boolean): Boolean = { 
    for { 
    i <- 0 until x 
    j <- i + 1 until x 
    if fn(i, j) 
    } return true 
    false 
} 

Но я чувствую, что return true не столь функциональный (или, может быть, это?). Есть ли способ переписать этот фрагмент кода более элегантным способом?

В целом, чем более функциональным (если есть) способ написать код типа return-early-from-a-loop?

+0

Вы можете взглянуть на хвостовую рекурсию – cchantep

ответ

4

Есть несколько способов может помочь, например, find, exists и т.д. В вашем случае, попробуйте следующее:

def foo2(x: Int, fn: (Int, Int) => Boolean): Boolean = { 
    (0 until x).exists(i => 
    (i+1 until x).exists(j=>fn(i, j))) 
} 
+0

Спасибо. Является ли этот ленивый/короткий номер? –

+0

@ ZizhengTai: Почему, по вашему мнению, он должен быть ленивым/закороченным? 'exists' вернется, как только первый элемент в последовательности удовлетворяет условию. – tuxdna

+0

Хорошо, что вы можете сжать его в одну строку '(0 до x) .exists (i => (i + 1 до x) .exists (j => fn (i, j)))' – tuxdna

2

Так как все, что вы проверяете на это существование, вы можете просто составить 2 использования exists :

(0 until x).exists(i => (i + 1 until x).exists(fn(i, _))) 

в целом, если вы обеспокоены больше, чем просто определение, если некоторый элемент существует, вы можете конвертировать ваши понимание в серии Streams, Iterators или views, вы можете использовать exists и он будет оценивать лениво, избегая ненужных выполнений цикла:

def foo(x: Int, fn: (Int, Int) => Boolean): Boolean = { 
    (for { 
    i <- (0 until x).iterator 
    j <- (i + 1 until x).iterator 
    } yield(i, j)).exists(fn.tupled) 
} 

Вы также можете использовать map и flatMap вместо for и toStream или view вместо iterator:

(0 until x).view.flatMap(i => (i + 1 until x).toStream.map(j => i -> j)).exists(fn.tupled) 

Вы также можете использовать view в любой коллекции, чтобы получить коллекцию, где все трансформаторы выполняются лениво. Это, возможно, самый идиоматический способ короткого замыкания обхода коллекции. От the docs on views:

Scala коллекция по умолчанию строгого во всех своих трансформаторах, за исключением Stream, который реализует все свои методы трансформаторных лениво. Тем не менее, существует систематический способ превратить каждую коллекцию в ленивую и наоборот, которая основана на коллекции просмотров. Вид представляет собой особый вид коллекции, который представляет собой базовую коллекцию, но лениво реализует все трансформаторы.

Что касается накладных расходов, это действительно зависит от особенностей! Различные коллекции имеют разные варианты: view, toStream и iterator, которые могут варьироваться в зависимости от объема накладных расходов. Если fn очень дорого вычислять, это накладные расходы, вероятно, стоит того, и сохранение вашего идиоматического функционального стиля для вашего кода делает его более удобным для обслуживания, отлаживаемым и читаемым. Если вы находитесь в ситуации, которая требует экстремальной оптимизации, вы можете вернуться к конструкциям нижнего уровня, таким как return (что не лишено собственных накладных расходов!).

+1

слишком сложно использовать потоки только для целых циклов? Я немного волнуюсь, что это приведет к слишком большим накладным расходам ... –

+0

@ZizhengTai Я добавил примечание, касающееся накладных расходов и некоторых других деталей. –

Смежные вопросы