25

私はHaskellで次の機能を持っています

agreeLen :: (Eq a) => [a] -> [a] -> Int
agreeLen x y = length $ takeWhile (\(a,b) -> a == b)  (zip x y)

私は、「慣用的な」Haskell の書き方を学ぼうとしています。これは、括弧の代わりに.andを使用すること$を好み、可能な場合はポイントフリー コードを好むようです。明示的xに言及することを取り除くことができないようです。y何か案は?

2つの引数の関数を無意味にすることで同じ問題が発生すると思います。

ところで、これは良いコードを書くことを追求しているだけです。「無意味にするために必要なものは何でも使用する」という宿題の練習ではありません。

ありがとう。


(コメントを追加) 回答ありがとうございます。あなたは、この関数がポイントフリーの恩恵を受けていないことを私に確信させました。また、式の変換を練習するための素晴らしい例もいくつか教えてくれました。私にはまだ難しく、C へのポインターと同じくらい Haskell に不可欠なようです。

4

4 に答える 4

48

また、可能な限り無意味なコードを優先します。

「可能な場合」ではなく、「可読性が向上する(または他の明らかな利点がある)場合」。

ポイントフリーにするには

agreeLen x y = length $ takeWhile (\(a,b) -> a == b)  (zip x y)

最初のステップは、($)右を移動し、あなたが持っているものをに置き換えることです(.):

agreeLen x y = length . takeWhile (\(a,b) -> a == b) $ zip x y

これで、さらに右に移動できます。

agreeLen x y = length . takeWhile (uncurry (==)) . zip x $ y

そこですぐに 1 つの引数を切り取ることができます。

agreeLen x = length . takeWhile (uncurry (==)) . zip x

次に、合成演算子の前置適用としてそれを書き換えることができます。

agreeLen x = (.) (length . takeWhile (uncurry (==))) (zip x)

そして、あなたは書くことができます

f (gx)

なので

f . g $ x

一般的に、ここで

f = (.) (length . takeWhile (uncurry (==)))

g = zipを与える

agreeLen x = ((.) (length . takeWhile (uncurry (==)))) . zip $ x

そこから引数xは簡単に削除されます。次に、接頭辞の適用を(.)セクションに変換して取得できます

agreeLen = ((length . takeWhile (uncurry (==))) .) . zip

しかし、それはオリジナルよりも読みにくいので、式をポイントフリー スタイルに変換する練習以外ではお勧めしません。

于 2012-11-17T00:51:10.947 に答える
12

以下を使用することもできます。

agreeLen :: (Eq a) => [a] -> [a] -> Int
agreeLen x y = length $ takeWhile id $ zipWith (==) x y

慣用的な Haskell は読みやすいものであり、必ずしも最も無意味なものではありません。

于 2012-11-17T00:43:25.560 に答える
1

別の簡潔でポイントのないソリューション:

agreeLen = ((length . takeWhile id) .) . zipWith (==)

同様に:

agreeLen = (.) (length . takeWhile id) . zipWith (==)
于 2018-06-29T06:39:05.383 に答える