def sum(xs: List[Int]): Int = {
if(xs.isEmpty)
0
else
xs.head + sum(xs.tail)
}
最後の行は誰でも説明できますか?スカラ:リストの再帰的集計
中間結果は に保存されます。xs.head + sum(xs.tail) と、その後に追加する要素が1つありますか?
def sum(xs: List[Int]): Int = {
if(xs.isEmpty)
0
else
xs.head + sum(xs.tail)
}
最後の行は誰でも説明できますか?スカラ:リストの再帰的集計
中間結果は に保存されます。xs.head + sum(xs.tail) と、その後に追加する要素が1つありますか?
再帰IMHOを説明する最善の方法は、段階的に進み、実際に何が起こっているかを見ることです。もう一つ役立つのは、return文を追加することです。特に、Javaのような言語から来た場合は、何が起こっているのかが分かりやすいためです。あなたのケースでは
def sum(xs: List[Int]): Int = {
if(xs.isEmpty)
return 0
else
return xs.head + sum(xs.tail)
}
リスト内のすべての整数を合計する機能を持っている:リターンと
あなたの関数は次のようになります。
だから、どのように振る舞うが機能します以下の値(1,2,3)
でリストを使用して、関数と呼ばれていることを想像することができますか?
まず、関数呼び出しは次のようになります。
if(xs.isEmpty) // false - it has 3 elements (1,2,3)
return 0 // skipped
else
return 1 + sum((2,3)) // xs.head is changed with 1 and xs.tail is list with (2,3)
第二コールリスト(2,3)となりました:
if(xs.isEmpty) // false - it has 2 elements (2,3)
return 0 // skipped
else
return 2 + sum((3)) // xs.head is changed with 2 and xs.tail is list with (3)
Trirdコールは、(3)リストを以下のようになります。
if(xs.isEmpty) // false - it has 1 elements (3)
return 0 // skipped
else
return 3 + sum(()) // xs.head is changed with 3 and xs.tail is empty list now
4回目の呼び出しは空のリストです:
if(xs.isEmpty) // true
return 0 // Recursion termination case triggered
は、だから今、私たちはこのような何か探して私たちの和コールスタックがあります。
sum((1,2,3))
where sum = 1 + sum((2,3))
where sum = 2 + sum((3))
where sum = 3 + sum(())
where sum = 0
をそして、我々は単に値を返す開始:
sum((1,2,3))
where sum = 1 + 5
where sum = 2 + 3
where sum = 3 + 0
は、私たちは1,2((合計で終わります、3))= 6
その理由は、「中間結果」を格納する必要がないのは、合計の計算が最後から始まり、後ろに向かって移動するためです。
実際の例について考えてみると役に立ちます。我々が持っていたとしましょう:
List(1,3,5)
は(リストが空でない)最初のテストは失敗しsum
方法、これを渡します。次に、尾部のsum
に先頭項目、つまりsum(List(3,5))
を追加します。したがって、2番目の式が計算されるまで、操作は完了することができず、sum
が2回呼び出されます。最初のテストは失敗し(List(3,5)
は空ではありません)、このメソッドは3 + sum(List(5))
の値を返します。再び2番目の式を計算するまでは完了できないので、sum
が再び呼び出されます。再び、List(5)
が空ではないため、最初のテストは失敗し、この呼び出しは5 + sum(List())
の値を返します。最後の時間のために、sum
はそう、と呼ばれ、この時間は、最初のテストは成功し、0を返します:
sum(List()) = 0
sum(List(5)) = 5
sum(List(3,5)) = 8
sum(List(1,3,5)) = 9
は、この種のものを考え出す再帰を理解するのに役立つ(必須)です。
中間結果(xs.headおよびsum(xs.tail))は、スレッドのJavaスタックを実行するメモリ領域であるフレームに格納されます。 sum関数のネスト呼び出しごとに別々のフレームが作成され、その結果がsum呼び出しごとに分離されます。 Java documentationから
:
フレームは、データおよび部分的な結果を格納するために、ならびに動的リンクを実行するために、メソッドの値を返し、ディスパッチ例外用いられます。
メソッドが呼び出されるたびに新しいフレームが作成されます。フレームは、そのメソッドの呼び出しが完了すると、その完了が正常であるか突然であるか(キャッチされない例外をスローする)のいずれであっても、破棄されます。フレームは、フレームを作成しているスレッドのJava Virtual Machineスタック(§2.5.2)から割り当てられます。各フレームには、ローカル変数(§2.6.1)、それ自身のオペランドスタック(§2.6.2)、および現在のメソッドのクラスの実行時定数プール(§2.5.5)への参照があります。 。ここで
は、あなたのコードは、JVMバイトコードにコンパイルする方法です:
public int sum(scala.collection.immutable.List<java.lang.Object>);
Code:
0: aload_1
1: invokevirtual #63 // Method scala/collection/immutable/List.isEmpty:()Z
4: ifeq 11
7: iconst_0
8: goto 30
11: aload_1
12: invokevirtual #67 // Method scala/collection/immutable/List.head:()Ljava/lang/Object;
15: invokestatic #73 // Method scala/runtime/BoxesRunTime.unboxToInt:(Ljava/lang/Object;)I
18: aload_0
19: aload_1
20: invokevirtual #76 // Method scala/collection/immutable/List.tail:()Ljava/lang/Object;
23: checkcast #59 // class scala/collection/immutable/List
26: invokevirtual #78 // Method sum:(Lscala/collection/immutable/List;)I
29: iadd
30: ireturn
注終わり近くIADD命令。命令をIADD のdescriptionから:
value1とvalue2の両方がint型である必要があります。 値はオペランドスタックからポップされます。 intの結果はvalue1 + value2です。結果はオペランドスタックにプッシュされます。
あなたの質問は不明です。どのような "中間結果"について話していますか?そして、「追加する単一の要素を提供する」とはどういう意味ですか?特に、「提供する」という意味と「追加する」という意味を正確に定義してください。 –
''リストの合計 '= 'リストの最初の要素' + 'リストの残りの合計'。 –
https://stackoverflow.com/questions/13544862/recursion-and-memoryは理解に役立ちます –