Ответ 1
Теперь, когда вы отредактировали, чтобы задать почти совершенно другой вопрос, я дам другой ответ. Вместо того, чтобы указывать на учебное пособие по картам и складкам, я просто отправлю его.
В Scala вам сначала нужно знать, как создать анонимную функцию. Это похоже на то, что от большинства общих до более конкретных:
(var1: Type1, var2: Type2, ..., varN: TypeN) => /* output */
(var1, var2, ..., varN) => /* output, if types can be inferred */
var1 => /* output, if type can be inferred and N=1 */
Вот несколько примеров:
(x: Double, y: Double, z: Double) => Math.sqrt(x*x + y*y + z*z)
val f:(Double,Double)=>Double = (x,y) => x*y + Math.exp(-x*y)
val neg:Double=>Double = x => -x
Теперь метод map
списков и т.д. будет применять функцию (анонимную или иначе) к каждому элементу карты. То есть, если у вас есть
List(a1,a2,...,aN)
f:A => B
затем
List(a1,a2,...,aN) map (f)
производит
List( f(a1) , f(a2) , ..., f(aN) )
Есть все основания, почему это может быть полезно. Возможно, у вас есть куча строк, и вы хотите знать, как долго каждый из них, или вы хотите сделать их все в верхнем регистре, или вы хотите их назад. Если у вас есть функция, которая делает то, что вы хотите для одного элемента, карта будет делать это со всеми элементами:
scala> List("How","long","are","we?") map (s => s.length)
res0: List[Int] = List(3, 4, 3, 3)
scala> List("How","capitalized","are","we?") map (s => s.toUpperCase)
res1: List[java.lang.String] = List(HOW, CAPITALIZED, ARE, WE?)
scala> List("How","backwards","are","we?") map (s => s.reverse)
res2: List[scala.runtime.RichString] = List(woH, sdrawkcab, era, ?ew)
Итак, эта карта в целом и в Scala.
Но что, если мы хотим собрать наши результаты? То, где складывается складка (foldLeft
является версией, которая начинается слева и работает правильно).
Предположим, что у нас есть функция f:(B,A) => B
, т.е. она берет B и A и объединяет их для создания B. Ну, мы могли бы начать с B, а затем подать наш список A в него в то время, и в конце всего этого у нас будет некоторое B. Это точно, что делает сгиб. foldLeft
делает это, начиная с левого конца списка; foldRight
начинается справа. То есть
List(a1,a2,...,aN) foldLeft(b0)(f)
производит
f( f( ... f( f(b0,a1) , a2 ) ... ), aN )
где b0
- это, конечно, ваше начальное значение.
Итак, возможно, у нас есть функция, которая принимает int и строку, и возвращает int или длину строки, в зависимости от того, что больше - если мы скроем наш список, используя это, это скажет нам самую длинную строку ( предполагая, что мы начинаем с 0). Или мы могли бы добавить длину к int, накапливая значения по мере продвижения.
Попробуем попробовать.
scala> List("How","long","is","longest?").foldLeft(0)((i,s) => i max s.length)
res3: Int = 8
scala> List("How","long","is","everyone?").foldLeft(0)((i,s) => i + s.length)
res4: Int = 18
Хорошо, хорошо, но что, если мы хотим знать, кто самый длинный? Один из способов (возможно, не самый лучший, но хорошо иллюстрирующий полезный шаблон) заключается в том, чтобы переносить как длину (целое число), так и ведущий соперник (строка). Пусть дают a go:
scala> List("Who","is","longest?").foldLeft((0,""))((i,s) =>
| if (i._1 < s.length) (s.length,s)
| else i
| )
res5: (Int, java.lang.String) = (8,longest?)
Здесь i
теперь является кортежем типа (Int,String)
, а i._1
является первой частью этого набора (Int).
Но в некоторых случаях, как это, использование складки не очень хочется, мы хотим. Если мы хотим, чтобы дольше двух строк, наиболее естественная функция была бы такой, как max:(String,String)=>String
. Как мы применим этот?
Ну, в этом случае есть "самый короткий" случай по умолчанию, поэтому мы можем сбросить функцию string-max, начиная с "". Но лучше использовать сокращение. Как и в случае с складками, есть две версии, одна из которых работает слева, а другая - справа. Он не принимает начального значения и требует функции f:(A,A)=>A
. То есть, он принимает две вещи и возвращает один и тот же тип. Вот пример с функцией string-max:
scala> List("Who","is","longest?").reduceLeft((s1,s2) =>
| if (s2.length > s1.length) s2
| else s1
| )
res6: java.lang.String = longest?
Теперь есть еще два трюка. Во-первых, следующие два означают одно и то же:
list.foldLeft(b0)(f)
(b0 /: list)(f)
Обратите внимание, что вторая короче, и это похоже на то, что вы принимаете b0
и делаете что-то в списке с ним (каким вы есть). (:\
совпадает с foldRight
, но вы используете его так: (list :\ b0) (f)
Во-вторых, если вы ссылаетесь только на переменную один раз, вы можете использовать _
вместо имени переменной и опустить часть анонимной функции x =>
. Вот два примера:
scala> List("How","long","are","we?") map (_.length)
res7: List[Int] = List(3, 4, 3, 3)
scala> (0 /: List("How","long","are","we","all?"))(_ + _.length)
res8: Int = 16
На этом этапе вы сможете создавать функции и отображать, складывать и уменьшать их с помощью Scala. Таким образом, если вы знаете, как ваш алгоритм должен работать, он должен быть достаточно простым для его реализации.