2016-03-31 17 views
0

私はちょうど(Scheme言語を使って)関数型プログラミングの学習を始めました。高次関数は、引数として別の関数を取り込む関数か、別の関数またはその両方を返す関数です。だから私は、高階関数に以下のコードを変換しようとしています:高次関数解明

;; define two procedures - one for calculating square & one for finding double of a number 
(define (square x) (* x x)) 
(define (double x) (+ x x)) 
(square 5) 
=> 25 
(double 5) 
=> 10 

は今、私は、以下の1思い付いた:

;; Implementation 2: 
    (define (applyToItself f x) (f x x)) 
    (define (square x) (applyToItself * x)) 
    (define (double x) (applyToItself + x)) 
    (square 5) 
=> 25 
    (double 5) 
=> 10 

私は、関数をとる関数applyToItselfと値を作成しました入ってくる関数を入力値に適用することによって計算された値を返します。 squareとdouble関数は、*と+を使用してapplyToItselfを使用します。

は最後に、私はこの実装の別の方法に出くわした:

;;Implementation 3: 
    (define (applyToItself f) (lambda(x) (f x x))) 
    (define square (applyToItself *)) 
    (define double (applyToItself +)) 

    (square 5) 
=> 25 
    (double 5) 
=> 10 

このapplyToItself実装は今だけの機能を取り込んで、他の関数ではなく、計算された値を返します。

私の質問は以下のとおりです。

  1. 実装2と実施3との間の有意な差や長所と短所がありますか?
  2. は、の にあります。実装2と実装3の両方が高次関数ですか?
  3. どちらが正しいまたはより良い実装ですか?

答えて

0
  1. ほとんど違いがありません。それは非常に簡単ではありませんが、バージョン2はカリングの一種であり、返されるプロシージャには、ラムダによって捕捉されたクロージャがsquareの間に存在する必要があるため、通常は再生されない変数があります。 doubleが存在します。

    コンパイラの中にはラムダリフティングと呼ばれる最適化手法があるものがあります。したがって、一部のコンパイラは、コンパイルプロセスの一部として実際にバージョン3をバージョン2に書き換えて最終結果を同一にします。

    プロシージャを生成するプロシージャがルックアップのような初期化を行うときには、これはバージョン3では1回しか発生しませんが、バージョン2ではすべてのアプリケーションで計算が行われます。

  2. はい

  3. v3は最も黒いボクシングので、より良い抽象化を持っていますが、私は多くの場合、使用量は、どちらか一方を指示していないの両方の道を行く示したもののような単純な例について。場合によってはv3を行う必要があります。例えば。明確な説明のためにmap

(map (applyToItself *) '(1 2 3)) ; ==> (1 4 9)

+0

感謝を使用して、リストのすべての要素を二乗。 Btw、あなたの最初のポイントで話している未使用の変数 'x'ですか? – VigneshBala

+0

@VigneshBala実際は 'f'です。例えば。'(as(* aa))(bs(* bb)))(sqrt(+ as bs)))' 'と' fsのように結果が返されたときに 'bs'は存在しません'変数がスコープから外れた後に使用されます。パパルのようないくつかの言語は、自由変数がもはやスタックに存在しない場合、下向きのfunarg(より深く使われる引数として関数を渡しますが、上向きには使用しません)をサポートしています。それは[funarg問題](https://en.wikipedia.org/wiki/Downward_funarg)と呼ばれています。 – Sylwester

関連する問題