7

私は関数型プログラミングを学んでおり、関数型スタイルでいくつかの問題を解決しようとしました。問題を関数に分割しているときに経験したことの 1 つは、2 つのオプションがあるように思われたことです。同様のパラメーター リストを持ついくつかの異なる関数を使用するか、クロージャーとして親関数のバインディングを単純に参照できるネストされた関数を使用します。

関数呼び出しが小さくなり、「感じ」が良くなったように見えたので、最終的には 2 番目のアプローチに行き着きましたが、私の読書では、関数型プログラミングの主要なポイントの 1 つが欠けている可能性があるように思えます。 -効果"? 確かに、これらのネストされた関数は、私が使用していた言語がそれを妨げているため、外部バインディングを変更できませんが、個々の内部関数を見ると、「同じパラメーターが与えられた場合、この関数は同じ結果を返す」とは言えません。親スコープの変数を使用するため...私は正しいですか?

どのような進め方が望ましいでしょうか。

ありがとう!

4

3 に答える 3

4

関数型プログラミングは、全か無かというわけではありません。関数をネストする方が理にかなっている場合は、そのアプローチを使用します。ただし、内部関数を純粋に機能させたい場合は、必要なすべてのパラメーターを明示的にそれらに渡します。

以下は、Scheme での小さな例です。

(define (foo a)
  (define (bar b)
    (+ a b))      ; getting a from outer scope, not purely functional
  (bar 3))

(define (foo a)
  (define (bar a b)
    (+ a b))      ; getting a from function parameters, purely functional
  (bar a 3))


(define (bar a b) ; since this is purely functional, we can remove it from its
  (+ a b))        ; environment and it still works

(define (foo a)
  (bar a 3))

個人的には、最初のアプローチを使用しますが、どちらでも同じように機能します。

于 2008-11-22T06:48:29.793 に答える
3

関数の入れ子は、多くの関数で作業を分割するための優れた方法です。それは実際には「副作用」ではありません。それが役立つ場合は、キャプチャされた変数を暗黙的なパラメーターと考えてください。

ネストされた関数が役立つ 1 つの例は、ループの置換です。ネストされた関数のパラメーターは、値を累積する誘導変数として機能できます。簡単な例:

let factorial n =
    let rec facHelper p n =
        if n = 1 then p else facHelper (p*n) (n-1)
    in
    facHelper 1 n

この場合、ユーザーはパラメーターfacHelperについて心配する必要がないため、グローバルのような関数を宣言しても意味がありません。p

ただし、ネストされた関数を個別にテストするのは難しい場合があることに注意してください。これらの関数は親の外部で参照できないためです。

于 2008-11-22T07:25:06.117 に答える
1

次の (不自然な) Haskell スニペットを検討してください。

putLines :: [String] -> IO ()
putLines lines = putStr string
    where string = concat lines

stringローカルにバインドされた名前付き定数です。しかし、それは引数をとらない関数でもあり、閉じてlinesいるため参照が透過的ではありませんか? (Haskell では、定数と nullary 関数はまったく区別できません!) このため、上記のコードは「副作用」または機能しないと考えますか?

于 2008-11-22T12:17:04.717 に答える