ほぼ完全に別の質問をするように編集したので、別の回答をします。マップと折り畳みに関するチュートリアルを示すのではなく、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) )
これが役立つ理由はさまざまです。たくさんの文字列があり、それぞれの長さを知りたい場合や、すべて大文字にしたい場合や、文字列を逆にしたい場合があります。1 つの要素に対して必要なことを行う関数がある場合、map はそれをすべての要素に対して行います。
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 ではそうです。
しかし、結果を収集したい場合はどうすればよいでしょうか? そこで、fold の出番です (これfoldLeft
は、左側から開始して右側で機能するバージョンです)。
関数 があるとしますf:(B,A) => B
。つまり、B と A を取り、それらを組み合わせて B を生成します。B から始めて、A のリストを一度に 1 つずつフィードすることができます。すべてが終わると、いくつかの B が得られます。それがまさに fold が行うことです。 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
わかりました、でも、誰が一番長いか知りたい場合はどうしますか? 1 つの方法 (最善ではないかもしれませんが、有用なパターンをよく示しています) は、長さ (整数)と主要な候補 (文字列) の両方を保持することです。試してみましょう:
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
は type のタプルに(Int,String)
なり、i._1
はそのタプル (Int) の最初の部分です。
しかし、このようないくつかのケースでは、折り畳みを使用したくない場合があります。2 つの文字列のうち長い方が必要な場合、最も自然な関数は のようなものになりmax:(String,String)=>String
ます。それをどのように適用しますか?
さて、この場合、デフォルトの「最短」ケースがあるため、「」で始まる string-max 関数を折りたたむことができます。しかし、より良い方法はreduceを使用することです。折りと同様に、2 つのバージョンがあり、1 つは左から機能し、もう 1 つは右から機能します。初期値をとらず、関数が必要ですf:(A,A)=>A
。つまり、2 つのものを取り、同じ型の 1 つを返します。string-max 関数を使用した例を次に示します。
scala> List("Who","is","longest?").reduceLeft((s1,s2) =>
| if (s2.length > s1.length) s2
| else s1
| )
res6: java.lang.String = longest?
さて、あと2つのトリックがあります。まず、次の 2 つは同じ意味です。
list.foldLeft(b0)(f)
(b0 /: list)(f)
b0
2 番目がどのように短いかに注意してください。これは、リストを使用して何かを実行しているという印象を与えます(それはあなたです)。(:\
と同じfoldRight
ですが、次のように使用します。(list :\ b0) (f)
次に、変数を 1 回だけ参照する場合は、変数名の代わりに を使用して、無名関数宣言_
の一部を省略できます。x =>
以下に 2 つの例を示します。
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 を使用してそれらをマップ、折り畳み、削減できるはずです。したがって、アルゴリズムがどのように機能するかを知っていれば、それを実装するのはかなり簡単です。