2016-07-08 4 views
0

В Scala, чтобы выразить это:Fold на вариант против вислоухая на коллекции

val opt:Option[String] = Some("a") 
if(opt.isEmpty) "" else opt.get 

вы можете использовать этот fold узор на Options:

opt.fold("")(_) 

Однако fold по коллекциям ведет себя по-разному. Как я могу добиться того же для коллекций, без необходимости писать:

case class Container(description: String, collection: Seq) 
val col = Seq() 
if(col.nonEmpty) { 
    Some(Container("some description", col)) 
} else { 
    None 
} 

Другими словами, как я могу элегантно возвращение Some() коллекции только тогда, когда оно не пусто?

+0

Вы попробовали 'headOption'? Или 'headOption.map (anygoeshere)' – maks

+0

Кроме того, 'if (opt.isEmpty)" "else opt.get' будет более идиоматичным с помощью' opt.getOrElse ("") ' – Dave

+1

Почему' if/else' не «элегантный»? Для меня это прекрасно. Если вы не хотите «if/else» по какой-либо причине, вот один из способов: 'Some (col) .filter (_. NonEmpty) .map (c => Container (« некоторое описание », c))' – sjrd

ответ

2

Обратите внимание, что ваши примеры Option и Seq не эквивалентны: в первом случае, результатом является значение, которое было внутриOption или пустая строка, в то время как в последнем случае, вы в конечном итоге возвращая коллекция сам, не его содержание.

Вы можете сделать эквивалент того, что вы сделали с Option, с fold в коллекциях, в зависимости от того, как вы хотите иметь дело с несколькими значениями в коллекции. Например:

seqOfStrings.foldLeft("") { _ + _ } 

бы сцепить все строки в коллекции в одну длинную строку (вы можете получить тот же результат с seqOfStrings.mkString). Обратите внимание, что это использует foldLeft, а не fold, потому что первый (предполагается) параллелизуемый и не гарантирует обработку результатов по порядку. Ниже приведен пример с fold:

seqOfInts.fold(0) { _ + _ } 

Это, конечно, такой же, как seqOfInts.sum

Идея fold (левая/правая) комбинирует («складной») все элементы коллекции в одно значение.Option - это особый случай, поскольку он может содержать только один элемент, поэтому функция трансформатора не должна принимать два аргумента и выглядит очень похожей на (противоположность) getOrElse - таким образом, ваше замешательство.

То, что вы пытаетесь сделать с коллекцией, на самом деле не используется для сгиба. Вы можете использовать headOption для того, как другие ответы предложили, или, еще лучше, шаблон матча:

col match { 
    case Seq() => None 
    case x => Some(Container("some description", x)) 
    } 

Вы можете обернуть его в объект компаньон, чтобы выглядеть красивее:

object Container { 
    def apply[T](seq: Seq[T]) = seq match { 
     case Seq() => None 
     case s => Container("some description", x)) 
    } 
    } 

Теперь вы можете просто сделайте Container(seq) в другом месте.

+0

Это очень приятно, я думаю, что контейнерный объект-компаньон является самым элегантным решением проблемы. Спасибо! Кроме этого, я думаю, что это немного глупо, что есть две функции, которые имеют одно и то же имя, но другое поведение, основанное на контексте ... – mirosval

+0

@mirosval Я не уверен, что вы подразумеваете под «другим поведением». Поведение на самом деле то же самое: вернуть начальное значение, если контейнер пуст, в противном случае применить преобразование к содержимому. – Dima

0

Это немного короче, хотя и не очень элегантна:

Option(col.nonEmpty).collect { case true => Container("some description", col) } 

Или, но IMO даже уродливее (я только увидел, что Хюсейна предложил это, а):

col.headOption.map(Container("some description", col)) 
1

Ваше ожидание от fold не так, это не о Option s о работе с коллекцией, и она работает с Option, потому что они также являются коллекциями.

С fold вы предоставляете начальное значение с первым параметром и предоставляете функцию, которая будет применяться к коллекции.

О вашем вопросе: кода вы поделились выглядит хорошо, но если вы действительно хотите отобразить некоторые опции вы можете использовать headOption и сопоставить его значение вы хотите, но это не сделает ваш код более элегантным или ясным