翻訳“関数型プログラミングが重要な理由” Haskellへ

StackOverflow https://stackoverflow.com/questions/1000145

  •  05-07-2019
  •  | 
  •  

質問

文化的および知的強化のために、私は少しHaskellを学ぶことにしました。 ヒューズの「なぜ機能プログラミングが重要なのか」を読んでいますそのコードを真のHaskellに変換しようとしています。以下にいくつかの試みを添付しました(論文の数値部分については、アルファベータアルゴリズムはさらに興味深いですが、ゲームエバリュエーターをゼロから作成する必要があります!)。

この時点では、他の何よりもHaskell構文の演習になっています。既に repeat をネイティブのHaskell iterate に変換し、多くの括弧を使用したいくつかの関数を合成して関数の構成(それらをよりポイントフリーにする)処理中)など。

しかし、私のコードは確かに動作し、それが十分に「Haskell-ish」かどうか疑問に思います。そこにいる専門家からヒントをいただけますか?

-- 4.1 Newton-Raphson square roots
next n x = (x + n/x)/2.0

-- -- this is "iterate::(a->a)->a->[a]"
-- repeat f a  = a : iterate f (f a)

within eps (a:b:rest) = 
  if abs(a-b) <= eps 
    then b
    else within eps (b:rest)

sqroot a0 eps n = within eps (iterate (next n) a0)

relative eps (a:b:rest) = 
  if abs(a-b) <= eps*abs(b)
    then b
    else relative eps (b:rest)

relativesqrt a0 eps n = relative eps (iterate (next n) a0)

-- 4.2 numerical differentiation

easydiff f x h = (f (x+h) - f x) / h

differentiate h0 f x = map (easydiff f x) (iterate (/2) h0)

-- diff1a h0 eps f x = within eps (differentiate h0 f x)
diff1 h0 eps f = within eps . differentiate h0 f 

elimerror n (a:b:rest) = (b*(2**n)-a)/(2**n-1) : elimerror n (b:rest)

-- need fromIntegral to make a non-integer out of the Int which comes out of round
order (a:b:c:rest) =  fromIntegral (round (logBase 2 ((a-c)/(b-c)-1)))

improve s = elimerror (order s) s 

--diff2a h0 eps f x = within eps (improve (differentiate h0 f x))
diff2 h0 eps f = within eps . improve . differentiate h0 f 

--super s = map second (iterate improve s) -- how can we make this point-free?
super :: (RealFrac t, Floating t) => [t] -> [t] 
           -- w/o this it wants to be [double]->[double]
super = map second . iterate improve

-- second (a:b:rest) = b
second = head . tail

diff3 h0 eps f = within eps . super . differentiate h0 f

-- 4.3 integration

easyintegrate f a b = (f a + f b)*(b-a)/2

-- addpair becomes (uncurry (+))

integrate f a b = integ f a b (f a) (f b) 

integ f a b fa fb = 
  (fa+fb)*(b-a)/2 : map (uncurry (+)) (zip (integ f a m fa fm) (integ f m b fm fb))
  where m = (a+b)/2 
        fm = f m 

-- test: following should be about pi
approxpi eps = within eps (improve (integrate (\x -> 4/(1+x*x)) 0 1))
superpi eps = within eps (super (integrate (\x -> 4/(1+x*x)) 0 1))

-- is there any way to keep track of the number of iterations? state monad, but seems like a lot of work...\

正しい解決策はありません

他のヒント

4.1ニュートンラプソン平方根

これらの2行

sqroot a0 eps n = within eps (iterate (next n) a0)
relativesqrt a0 eps n = relative eps (iterate (next n) a0)

ほとんど同じなので、さらに一歩先を行くことができます:

sqroot method a0 eps n = method eps (iterate (next n) a0)
relativesqrt = sqroot relative
withinsqrt   = sqroot within

4.2数値微分

h0 differentiate 関数の引数として持つことは、 0 の開始点にすぎないため、意味がありません制限シーケンス。 (開始点が重要となる可能性があるニュートン・ラプソンの場合の a0 についても同様ではありません。)

この制限がゼロに近づく割合を抽象化することも同様に適切だと思います:

differentiate rate f x = map (easydiff f x) (iterate rate 1)

もちろん、両方を実行できます:

differentiate rate h0 f x = map (easydiff f x) (iterate rate h0)

いずれにせよ、それは非常にarbitrary意的な決定です。

4.2統合

使用できます

zipWith (+) (integ f a m fa fm) (integ f m b fm fb)

の代わりに
map (uncurry (+)) (zip (integ f a m fa fm) (integ f m b fm fb))

読みやすいと思います。

within および relative の場合、保護された表記法を使用します。

within eps (a:b:rest)
  | abs(a-b)<=eps = b
  | otherwise = within eps (b:rest)

second の場合、 !!と書くことができます!! 1 。特に最後の1つは個人的な好みです。

型シグネチャも指定する必要があります。

編集:難読化が必要な場合は、次を試してください:

within :: (Ord a, Num a) => a -> [a] -> a
within eps l@(_:xs) = snd. head . filter ((<= eps) . fst) $ zip zs xs
   where
    zs = zipWith (\ a b -> abs (a-b)) l xs

(タイプはチェック済み、テストされていない-フィルターが正しいかどうか、または否定する必要があるかどうかはわからない;)

ロジャー・コステロは、ジョン・ヒューズ論文の2部構成の要約を書きました。 a>元のMirandaコードをHaskellに翻訳します。 パート1 およびパート2 の記事。

それで、これは真っ直ぐな翻訳(つまり、コードをできるだけ似たように保つことを試みる)または埋め込み(つまり、例を理解しにくくすることなく、コードに可能な限り多くの慣用的な調整を加える) 、もちろん)1つですか?

この「プロジェクト」がまだ続いています......

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top