関数型プログラミングはまったく違うのですか、それとも実際にはもっと難しいのですか?


12

関数型プログラミングはまったく違うのですか、それとも実際にはもっと難しいのですか?

プログラミングをまったく学んだことがなく、関数型プログラミングを教えられた人のことを言ってください。vsプログラミングをまったく学んだことがなく、命令型プログラミングを教えられた人。彼はどちらが厳しいでしょうか?または同じ?

私の質問:今の問題は入力をキャメルケースにすることだと言って、

そのようにqwe_asd_zxc_rty_fgh_vbnなるqweAsdZxcRtyFghVbn

手順は次のとおりです。

  1. に沿って分割する _
  2. 最初の項目をスキップして配列をループします
  3. エントリごとに最初の文字を大文字にします
  4. 結果を結合する

機能的な方法は次のとおりです。

  1. _返品が見つからない場合input
  2. input最初に沿ってカットします_(取得するqweおよびasd_zxc_rty_gfh_cvb
  3. の最初の文字を大文字にし、headそれを連結しますf(tail)

あなたが持っている場合は[OK]を機能的なバックグラウンドをと手続きプログラミングにおける豊富な経験を持って、私はお願いしたいと思います:それは、手続きの方法を見つけ出すにお時間がかかりますか、それは機能的な方法を把握するためにあなたのために長い時間がかかりますか?

あなたが持っている場合は、手続き-背景をが、機能、プログラミングで長年の経験を持って、私は同じ質問をしたいと思います:それは、手続きの方法を見つけ出すにお時間がかかりますか、それは、機能を把握するために、あなたのために時間がかかります仕方?


5
えーと、map手順3で変更ループの代わりに使用する場合、「手順」は完全に機能しているように見えます。2番目のアプローチは、標準ライブラリに分割関数がない場合にのみ検討するものです(この場合、これも使用しない命令型ソリューションと比較する必要がありますsplit)。
sepp2k

8
どちらの例についても、特に機能的または手続き的なものはありません。関数型プログラミングの誤った理解から外挿しようとしているように思えます。異常な結果が得られているのも不思議ではありません。
ラインヘンリヒス

関数型プログラミングは難しいとは思いませんが、ただ違うだけです。プログラミングの経験がない場合でも簡単に習得できますが、何らかの理由で命令型プログラミングを習得すると、関数型プログラミングが難しくなります。
dan_waterworth

1
JavaScript、C#、Groovyなどの主流の言語にはますます多くの機能的な機能が含まれているため、機能と手続きの区別は重要ではないと思います。
user281377

2
命令型プログラミングは、以前にプログラミングの経験がなかった人にとってはるかに難しく、直感に反します。のような表現はx=x+1、予期しない脳を爆破できます。関数型プログラミングは自然であり、純粋で便利な厳密な数学関数にすぎません。
SKロジック

回答:


12

ただ違う。関数型プログラミングは、ほとんどの人がよく知っている数学とより密接に関連しています。「不変の変数」全体は、「可変」の考え方が深く染み込んでいる命令型プログラマーに衝撃を与えるだけです。

初心者にとっては、何かの価値変えることはできないということは、かなり直感的です。

私がCSを学んだところで、最初のコースとして関数型言語を教えられました。そして、C ++やJavaを習得したすべての人は、以前はC ++やJavaに苦労していました。プログラミングに慣れていない人は、かなり簡単にそれを見つけました。


jalfあなたはプログラミングの初心者の一人で、かなり簡単に手に入れましたか?
Pacerier

私はその中間にいた。それまではC ++とPHPを少しいじっていましたが、命令的な考え方に慣れるには十分ではありませんでした。クラス全体を見ると、そのパターンはかなり明確でした。また、これは10年近く前だったので、いや、今日のプログラミングはまったく新しいものではありません;)
jalf

不変変数?数学は関数型プログラミング言語ではありませんか?数学の変数は確かに可変ですよね?
user56834

20

ただ違う

プログラミングするとき、基本的に、推論する方法をコードに変換します。あなたの考えと最終的な解決策の間の距離は、「認知のギャップ」と言われるかもしれません。ギャップが大きいほど、橋渡しが難しくなります。

手続きのバックグラウンドから来た場合は、手続き型で考えるように訓練されているので、ギャップは機能コードの場合よりも小さくなり、逆の場合も同様です。

プログラミングパラダイムを他の何よりも本質的に簡単にする唯一の方法は、通常の言語のように、既に知っているものにマッピングする場合です。そのため、短いギャップから始めます。

とにかく機能的および手続き的はかなり流動的な概念であり、重複する傾向があります


4

はい、多くの人にとって関数型プログラミングは理解しにくい傾向があります(特に、手続き型プログラミングに最初にさらされたことがある人は特にそうです)。

関数型プログラミングの例は、実際には関数型プログラミングの非常に良い例ではありません。再帰を使用し、状態を変更する代わりに結果を作成するだけですが、それ以上のことはありません。

関数型プログラミングのより良い例を得るために、より一般的な問題を考えてください。「アンダースコアを検索して次の文字を大文字に変換する」のではなく、パターンを検索して、見つかった。

多くの言語がこれをサポートしていますが、そのためには、パターンを正規表現のようなものとして指定する必要があります。ただし、正規表現は特別な目的のプログラミング言語に過ぎず、RE実装はその言語のコンパイラーやインタープリターです。REをコンパイルした結果は、基本的に(特別なRE仮想マシンで)実行され、何らかの入力に対して式を照合する関数です。

Perlのようなものでは、特殊な言語を使用してパターンを指定し、特殊なコンパイラーを使用してその文字列を何らかの関数のようなものに変換し、特殊なインタープリターを使用してその関数のようなものを実行します。関数型言語では、通常、言語自体を使用してパターンを指定し、言語独自のコンパイラーを使用して実際の関数を生成します。その関数をオンザフライで生成できます(必要に応じてREをコンパイルできる程度)が、実行すると、結果は特別なREを必要とせずに言語の他の関数のように実行できます。

結果は、私たちがいることであることができ、比較的簡単に上記の問題を一般化します。ただし、「_」と「大文字」を変換に直接ハードコーディングする代わりに、次のようなものを使用できます。

s&r(pattern, transform, string) {
    if (!pattern(string))
        return string
    else
        return transform(matched part of string) + s&r(rest of string);
}

ただし、パターンをREとして指定するものとは異なり、パターンを実際の関数として直接指定し、引き続き使用することができます。

my_pattern(string) return beginning(string) == '_';

そして、その関数をs&rに渡します。今のところ、それは非常に簡単な関数であり、完全に静的にエンコードしました。関数型言語は、REのように使用し、ユーザー入力などに基づいてまったく新しい関数をオンザフライで生成する場合に非常に興味深いものになりますが、REとは異なり、その関数は実行するために特別なREインタープリターを必要としません-他のような通常の機能です。


4

ラケットの完全なコードは次のとおりです。

;; camelize : string -> string
(define (camelize str)
  (let ([parts (regexp-split #rx"_" str)])
    ;; result of regexp-split is never empty
    (apply string-append
           (first parts)
           (map string-titlecase (rest parts)))))

(camelize "qwe_asd_zxc_rty_fgh_vbn")
;; => "qweAsdZxcRtyFghVbn"

手続き型の経験を持つ機能的なプログラマーとして、手続き型のソリューションを「図式化」するのに時間がかかるとは思いませんが、間違いなく入力に時間がかかります。

ところで、元の投稿で期待される結果の例は間違っています。末尾近くに「h」がありません。


それを指摘するためのgd。編集
Pacerier

3

私のペット理論は、プログラミングモデルは実際のコンピューターの動作に近いほど理解しやすいということです。ポインターは、本質的にマシンアドレスであることに気付くまで理解するのが困難です。再帰を理解するのは、あなたが意識して小さな例を通り抜け、スタックフレームを見て、同じ変数の異なる値が格納されている場所に気付くまで理解するのは困難です。それは、アセンブラープログラミングが高レベルプログラミングよりも簡単であることを意味するものではありませんが、それがどのように行われるかを見たことは、プログラミングまたは一般的な使いやすさのいずれにおいても、習熟度の鍵となるメンタルモデルを驚かせます。

現在、手続き型モデルは通常のマシンアーキテクチャにやや近づいています。割り当てはメモリ(またはレジスタ)の書き込みです。プロシージャコールは、実際には単なるジャンプ、if条件付きジャンプなどです。しかし、Lispには、たとえば、字句バインディングまたはラムダ式に相当する単純な低レベルはありません。それを理解するには、言語レベルと物理マシンの間で完全に独立した抽象的な機能マシンを想像する必要があります。

(フォン・ノイマンのアーキテクチャは最終的にarbitrary意的であるという考え精通しており、初心者の心を機械アーキテクチャのこのような無関係な詳細に偏見させずに、プログラミング言語のセマンティクスに直接導入するべきではありません。実際、しかし、これは高貴であるが見当違いの目標であると感じています。人々はボトムアップから理解を深めることでプログラミングを学び、関数型プログラミングへの道はもう少し長くなります)


7
そのロジックにより、アセンブラーはすべての言語の中で最も簡単に学習できるはずです:)
Homde

4
関数型プログラミングは、正しい方向からやってくると理解しやすくなります。あなたが言う「抽象的な機能的な機械」は、単に代数です。評価は用語の書き換えによって行われます。関数の適用は置換によって行われます。プログラマーは、長年の数学の授業ですでに見たものと同じツールを使用して問題を解決することを学びます。彼らがCSを追求するなら、後でカメの山に会うのに十分な時間があります。そうでない場合、彼らはまだ有用な問題解決スキルと設計原則を学びました。見てみましょうデザインプログラムにどのように
ライアンカルペッパー

2
@mkoいいえ、このロジックにより、実際のバイトコード011011001001101...は学習するのが最も簡単な言語になります!
MarkJ

2
@konrad:RISCアセンブラーはおそらく最も習得しやすい言語です。何か有用なことをする方法を知ることは、別の話です
ブライアン・ベッチャー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.