Haskellで議論を「再利用」するためのトリック?
-
30-09-2019 - |
質問
時々、「最後の引数を2回使用してください」という問題につまずき、例えば、ポイントフリースタイルを書くか、ラムダを避けるために。例えば
sqr x = x * x
と書くことができます
sqr = doubleArgs (*) where
doubleArgs f x = f x x
または、このやや複雑な機能を考慮してください(から取られた この質問):
ins x xs = zipWith (\ a b -> a ++ (x:b)) (inits xs) (tails xs)
このような関数があれば、このコードポイントフリーを書くことができます。
ins x = dup (zipWith (\ a b -> a ++ (x:b))) inits tails where
dup f f1 f2 x = f (f1 x) (f2 x)
しかし、HoogleでDoubleargsやDupのようなものを見つけることができないので、ここでトリックやイディオムを見逃すかもしれないと思います。
解決
から Control.Monad
:
join :: (Monad m) -> m (m a) -> m a
join m = m >>= id
instance Monad ((->) r) where
return = const
m >>= f = \x -> f (m x) x
拡大する:
join :: (a -> a -> b) -> (a -> b)
join f = f >>= id
= \x -> id (f x) x
= \x -> f x x
だから、ええ、 Control.Monad.join
.
ああ、そしてあなたのポイントフリーの例のために、あなたはアプリケーション表記を使用してみましたか(から Control.Applicative
):
ins x = zipWith (\a b -> a ++ (x:b)) <$> inits <*> tails
(なぜ人々がとても好きなのかわかりません a ++ (x:b)
それ以外の a ++ [x] ++ b
...それはより速くありません - インライナーがそれを処理します - そして、後者ははるかに対称的です!しかたがない)
他のヒント
あなたが「doubleargs」と呼ぶものは、より頻繁にdupと呼ばれます - それはWコンビネーター(mockingbirdをmockto mockbird in to mockbird) - 「The Elementary Duplicator」です。
あなたが「dup」と呼ぶのは、実際には「スターリングプライム」コンビネーターです。
Haskellにはかなり小さい「コンビネーターの基礎」があります。データを参照してください。さらに、いくつかのアプリケーションおよびモナディック操作は、アプリケーションとモナドの関数インスタンスにより、より多くの「標準」組み合わせを追加します(<*>は、s-スターリングコンビネーターです。機能的インスタンス、lifta2&liftm2はスターリングプライムです)。データを拡大するためにコミュニティにはあまり熱意がないように思われます。機能しているため、コンビネーターは楽しいですが、実用的には、コンビネーターが直接利用できない状況で長い手を好むようになりました。
これが私の質問の第2部の別の解決策です:矢印!
import Control.Arrow
ins x = inits &&& tails >>> second (map (x:)) >>> uncurry (zipWith (++))
&&&
( "Fanout")引数を2つの関数に分配し、結果のペアを返します。 >>>
( "and")関数アプリケーションの順序を逆転させます。これにより、左から右に一連の操作が可能になります。 second
ペアの第2部でのみ動作します。もちろん、あなたは必要です uncurry
最後に、2つの引数を期待する関数でペアを供給します。