「状態」が許容されない場合、関数型言語を使用して命令型言語ですべてを実行できますか?


7

私はMITのコンピュータープログラムの構造と解釈(SICP)を読んでいました。私が理解したのは、純粋な関数型プログラミング言語では、ローカルな状態などは存在しないということです。SICP、pg 230は言う:

「この本の最後の2つの章で行ったように、割り当てを使用しないプログラミングは、関数型プログラミングと呼ばれています。」

私は命令型プログラミングのバックグラウンドを持っており、実際のシステム(単純な銀行やライブラリ管理ソフトウェアなど)をモデル化して、状態を意識せずにプログラムを作成する方法を理解できていないようです。

関数型プログラミングに状態の概念がない場合、どうすればよいですか?

回答:


8

関数型プログラミングの鍵は、状態がないことではなく、明示的な状態があることです。

つまり、状態はパラメータとして関数に渡されます。これは実際の値であり、実際に使用して、見て、他の関数に渡すことができます。

たとえば、フィボナッチ数列を計算する動的計画法を見てみましょう。命令型言語では、次のようなものになります。

def fib(n):
  A = {}
  A[0] = 0
  A[1] = 1
  for i in [2 .. n+1]:
      A[i] = A[i-1] + A[i-2]
  return A[n]

状態なしでこれを行うには、ストアを明示的に渡す必要があります。Haskell風の構文を使用する:

fib n = fibHelper 2 n {(1,1), (0,0)}

fibHelper i end cache = 
  if 
    i > end
  then 
    lookup end cache
  else
    let
      newVal = (lookup (i-1) cache) + (lookup (i-2) cache)
      newCache = insert i newVal cache
    in
      fibHelper (i+1) end newCache

フィボナッチ数列の配列全体が必要ないため、これは少し工夫されていますが、以前に計算された値のセット全体が必要なナップザックなどのより複雑な動的プログラミング問題にこれを使用することを想像できます。

ここで理解しておくべき重要なことinsertは、ストアを取り、新しい値を追加した元のストアと等しい新しいストアを返す関数であることです。の元の値はcache破棄されないため、何らかの「取り消し」操作が必要なアプリケーションがある場合は、状態の履歴を追跡できます。

「しかし、それは非効率に見えます。毎回まったく新しいストアを作成しているのです!」ただし、通常、関数型言語では、これらは参照を使用して巧妙に実装されているため、データの新しいコピー全体が存在することはありません。

また、状態パラメータを設定し、計算の進行に応じて渡し、変更するというこのパターンは非常に一般的であることにも言及する価値があります。人々はStateモナドのような抽象化を発明しました。これにより、必須のように見えるが、純粋に「内部」で機能するものを書くことができます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.