私はPythonで次のように練習します:
多項式は、累乗がインデックスによって決定されるような係数のタプルとして与えられます。例:(9,7,5)は、9 + 7 * x + 5 * x ^ 2を意味します
与えられたxの値を計算する関数を書く
最近関数型プログラミングに夢中になっているので、私は書きました
def evaluate1(poly, x):
coeff = 0
power = 1
return reduce(lambda accu,pair : accu + pair[coeff] * x**pair[power],
map(lambda x,y:(x,y), poly, range(len(poly))),
0)
私は判読できないと思うので、私は書きました
def evaluate2(poly, x):
power = 0
result = 1
return reduce(lambda accu,coeff : (accu[power]+1, accu[result] + coeff * x**accu[power]),
poly,
(0,0)
)[result]
少なくとも読めないので、私は書きました
def evaluate3(poly, x):
return poly[0]+x*evaluate(poly[1:],x) if len(poly)>0 else 0
これはあまり効率的ではないかもしれません(編集:私は間違っていました!)指数の代わりに多くの乗算を使用するため、原則として、ここでは測定については気にしません(編集:私の愚かさ!測定は私の誤解を指摘したでしょう!)まだ、反復的なソリューションほど(おそらく)読み取り可能ではありません。
def evaluate4(poly, x):
result = 0
for i in range(0,len(poly)):
result += poly[i] * x**i
return result
命令型と同じくらい読みやすく、効率が近い純粋に機能的なソリューションはありますか?
確かに、表現の変更は役立ちますが、これは演習によって与えられました。
Pythonだけでなく、HaskellまたはLISPにすることもできます。
@delnanが指摘しているように、Hornerの方法はおそらくより計算効率が良いですが、指数解法では、これをPythonでかなり読みやすくしています:
def eval_poly(poly, x):
return sum( [a * x**i for i,a in enumerate(poly)] )
多くの関数型言語にはmapiの実装があり、マップを介してインデックスを作成できます。それを合計と組み合わせると、F#に次のようになります。
let compute coefficients x =
coefficients
|> Seq.mapi (fun i c -> c * Math.Pow(x, (float)i))
|> Seq.sum
justに(固定された)タプルがある場合、なぜこれを行わないのか(Haskellで):
evalPolyTuple (c, b, a) x = c + b*x + a*x^2
代わりに係数のリストがある場合は、以下を使用できます。
evalPolyList coefs x = sum $ zipWith (\c p -> c*x^p) coefs [0..]
またはあなたが持っていたように削減して:
evalPolyList' coefs x = foldl' (\sum (c, p) -> sum + c*x^p) 0 $ Zip coefs [0..]
定義した問題のスコープとコードの関係がわからないので、(記述した命令コードに基づいて)問題のスコープを無視してコードの機能のバージョンを提供します。
かなり読みやすいhaskell(このアプローチは、リストの構造化があり、純粋で読みやすい、あらゆるFP言語に簡単に変換できます):
eval acc exp val [] = acc
eval acc exp val (x:xs) = eval (acc + execPoly) (exp+1) xs
where execPoly = x * (val^exp)
そのようなhaskellの素朴なシンプルなアプローチは、FPに慣れていない人々へのより簡潔なアプローチよりもクリーンな場合があります。
まだ完全に純粋な、より明確な命令的アプローチは次のとおりです。
steval val poly = runST $ do
accAndExp <- newSTRef (0,1)
forM_ poly $ \x -> do
modifySTRef accAndExp (updateAccAndExp x)
readSTRef accAndExp
where updateAccAndExp x (acc, exp) = (acc + x*(val^exp), exp + 1)
2番目のアプローチのボーナスは、STモナドにあり、非常にうまく機能します。
確かですが、Haskellerが実際に実装する可能性が最も高いのは、上記の別の回答で述べたzipwithです。 zipWith
は非常に典型的なアプローチであり、Pythonは、関数とマップ可能なインデクサーを組み合わせるというジッピングアプローチを模倣できると思います。
関数アルゴリズムの可読性を向上させるために使用できる一般的な一連のステップがあります。
evaluateTerm
のようなものを読む方がはるかに簡単です。 canラムダを使用するからといって、必ずしもshouldであるとは限りません。enumerate
またはzipWith
を再発明したようです。