4

私が見る限り、Scheme を使用した関数型プログラミングで反復構造を教える通常の (そして私の意見では最良の) 順序は、最初に再帰を教えてから、map、reduce、およびすべての SRFI-1 手順などに取りかかることです。これはおそらく、再帰により、学生は反復に必要なすべてのものを持っているためです (また、必要に応じて SRFI-1 のすべてを書き直すことさえできます)。

SRFI-1 のいくつかの手順を使用し、それらが十分でない場合 (たとえば、関数を近似する場合) にのみ再帰を使用するという、逆のアプローチが試みられたことがあるかどうか疑問に思っていました。結果は良くないと思いますが、このアプローチの過去の経験について知りたいです。

もちろん、これは Scheme に固有のものではありません。この質問は、関数型言語にも当てはまります。

再帰の前に「アプリケーション プログラミング」(コンビネータの使用) を教えている本の 1 つに、Dave Touretsky のCOMMON LISP: A Gentle Introduction to Symbolic Computationがあります。

4

7 に答える 7

6

IMOは、最初に基本的な知識ブロックから始めて、次に結果を導き出す方が優れています。これは彼らが数学で行うことです。つまり、前者はそれぞれ後者から派生しているため、乗算の前にべき乗を導入せず、加算の前に乗算を導入しません。何人かのインストラクターが逆に行くのを見たことがありますが、基本から結果に移るときほど成功していないと思います。さらに、より高度なトピックを遅らせることで、生徒がすでに持っている知識を使用してこれらの結果を導き出すための精神的な課題を生徒に与えます。

于 2010-06-03T12:01:07.663 に答える
3

「再帰により、学生は反復に必要なすべてのものを持っている」と言うには、根本的に欠陥があります。(再帰的) 関数の書き方を知っていれば何でもできるというのは事実ですが、学生にとって何が良いのでしょうか? 考えてみると、機械語を知っていれば必要なものはすべて揃っています。もっと極端なことを言えば、CPU とワイヤーの山を渡せば、必要なものはすべて揃っています。

はい、それは大げさですが、人々の教え方に関係している可能性があります. 任意の言語を使用して、不要な構成要素を削除します。Scheme のような関数型言語でこれを行う極端な場合、ラムダ計算 (または同様のもの) が残されますが、これは必要なものすべてです。しかし明らかに、より多くのテリトリーをカバーする前に、初心者をそのポットに放り込むべきではありません。

これをより具体的な観点から考えるために、リストペアの違いを考えてみてください。それらはSchemeで実装されているからです。リストがペアとしてどのように実装されているかを知らなくても、リストだけで多くのことができます。実際、2 番目の引数として適切なリストを必要とする の限定された形式を学生に与える場合、consリストの実装方法の詳細に進む前に、理解しやすい一貫した概念を導入することで学生に有利になります。 . consこのようなことを教えた経験がある場合は、必要な場所を使用することについてどうしようもなく混乱している多くの学生に遭遇したと確信しています。appendおよびその逆。両方を必要とする問題は、初心者にとって非常に困難な場合があり、世界中のすべてのボックス + ポインター図が初心者の役に立ちません。

したがって、実際の提案をするために、HtDPをご覧になることをお勧めします。この本が非常に慎重に行っていることの 1 つは、学生を徐々にプログラミングにさらすことです。生徒はその時点で知っています。

于 2010-06-03T12:38:37.203 に答える
2

私はこの順序が教育で使われるのを見たことがありません、そして私はそれがあなたと同じくらい後ろ向きであると思います。StackOverflowにはかなりの数の質問があり、少なくとも一部のプログラマーは、「関数型プログラミング」は専ら「魔法の」コンビネーターのアプリケーションであり、必要なものがあったとしても、必要なコンビネーターが存在しないと途方に暮れると考えていることを示しています。 map3と同じくらい簡単です。

この偏見を考慮して、生徒がコンビネータを紹介する前に自分で書くことができるようにします。

于 2010-06-03T12:00:46.527 に答える
2

また、再帰の前にmap/reduceを導入するのも良い考えだと思います。(ただし、従来のSICPは最初に再帰を導入し、リストと再帰に基づいてmap / reduceを実装します。これはボトムアップアプローチからの建物の抽象化です。その重点は依然として抽象化です。)

F#/MLを使用して共有できる二乗和の例を次に示します。

let sumOfSqrs1 lst = 
    let rec sum lst acc =
        match lst with
            | x::xs -> sum xs (acc + x * x)
            | [] -> acc
    sum lst 0

let sumOfSqr2 lst = 
    let sqr x = x * x
    lst |> List.map sqr |> List.sum

2番目の方法は、この二乗和の問題を実行するためのより抽象的な方法ですが、最初の方法は、あまりにも多くの詳細を表現します。関数型プログラミングの強みは、より優れた抽象化です。Listライブラリを使用する2番目のプログラムは、forループを抽象化できるという考えを表しています。

生徒がで遊ぶことがList.*できれば、これらの機能がどのように実装されているかを知りたがります。そのとき、再帰に戻ることができます。これは一種のトップダウン教育アプローチです。

于 2010-06-03T12:01:40.037 に答える
1

これは悪い考えだと思います。
再帰は、プログラミングにおいて理解するのが最も難しい基本的な主題の1つであり、使用するのはさらに困難です。これを学ぶ唯一の方法はそれをすることであり、それはたくさんあります。
生徒にうまく抽象化された高階関数が渡される場合は、これらを再帰的に使用し、高階関数を使用するだけです。そうすれば、彼らが自分で高階関数を書く必要があるとき、彼らは無知になり、教師であるあなたが彼らのために実際にコードを書く必要があります。誰かが言ったように、人々に主題とそれを彼らのニーズに合わせてカスタマイズする方法を本当に
理解 してもらいたいのなら、あなたは主題をボトムアップで学ばなければなりません。

于 2010-06-03T13:30:35.210 に答える
0

私は、機能的にプログラミングするとき、多くの人が命令型スタイルを「模倣」し、ループに似たものを必要とせず、代わりにマップまたはフォールド/リデュースが必要な場合に、再帰を使用してループを模倣しようとすることがよくあります。

ほとんどの関数型プログラマーは、命令型スタイルを模倣しようとするべきではないことに同意するでしょう。関数はそれ自体で定義されます。再帰は「ループ」ではなく、「関数自体を定義する」と見なすべきです。

ただし、マップは同じことの繰り返しです。ループをシミュレートするために (末尾の) 再帰を使用する場合は、機能的なスタイルでマップを使用する必要があります。

于 2010-06-09T02:32:17.593 に答える
-1

問題は、あなたが本当に教えたい/学びたい「再帰」は、最終的には、技術的には再帰ではなくループである末尾再帰であるということです。

だから私は、先に進んで本当の再帰を教え/学び、それが役に立たない理由を教え、次に末尾再帰を教え、そして再帰ではない理由を教えます。

それが最善の方法のように思えます。学習している場合は、高階関数を使いすぎる前にこれらすべてを行ってください。教えている場合は、ループをどのように置き換えるかを彼らに示してください (そうすれば、後で末尾再帰を教えるときに、ループが実際には隠されているだけでまだそこにあることを理解するでしょう)。

于 2010-06-04T12:31:27.580 に答える