22

関数内の引数の順序を変更したい場合はどうすればよいですか?

ありますflip

flip :: (a -> b -> c) -> b -> a -> c

しかし、より多くの引数に対して機能させる方法がわかりません。引数を並べ替える一般的な方法はありますか?

4

2 に答える 2

40

関数を書いた後に編集したい場合は、Conal Elliott の優れたブログ投稿セマンティック エディター コンビネーターを読む必要があります。

http://conal.net/blog/posts/semantic-editor-combinators

実際、とにかく誰もがそれを読むべきです。これは本当に便利な方法です (ここでは悪用しています)。Conal は、非常に柔軟な効果を得るためにresult、より多くの構造を使用します。flip

result :: (b -> b') -> ((a -> b) -> (a -> b'))
result =  (.)

3 つの引数を使用する関数があるとします。

use3 :: Char -> Double -> Int -> String
use3 c d i = c: show (d^i)

最初の2つを交換したいのflip use3ですが、あなたが言うように使用しますが、2番目と3番目を交換したい場合は、最初の引数に適用flip した結果に適用する必要があります.use3

use3' :: Char -> Int -> Double -> String
use3' = (result) flip use3

5 を使用する関数の 4 番目と 5 番目の引数を入れ替えてみましょうuse5

use5  :: Char -> Double -> Int -> (Int,Char) -> String     -> String
use5' :: Char -> Double -> Int -> String     -> (Int,Char) -> String

use5 c d i (n,c') s = c : show (d ^ i) ++ replicate n c' ++ s

flip最初の 3 つの引数に適用した結果に適用する必要がuse5あるため、それが結果の結果の結果です。

use5' = (result.result.result) flip use5

考えを後回しにして定義してみませんか

swap_1_2 :: (a1 -> a2 -> other) -> (a2 -> a1 -> other)
swap_2_3 :: (a1 -> a2 -> a3 -> other) -> (a1 -> a3 -> a2 -> other)
--skip a few type signatures and daydream about scrap-your-boilerplate and Template Haskell    

swap_1_2 = flip    
swap_2_3 = result flip
swap_3_4 = (result.result) flip
swap_4_5 = (result.result.result) flip
swap_5_6 = (result.result.result.result) flip

...そして、シンプルさとエレガンスが好きなら、ここでやめるべきです。素晴らしい Curry と の右結合性により、型otherがそうなる可能性があることに注意してください。swap_2_3 は、2 つを超える任意の数の引数を取る関数に対して機能します。より複雑なものについては、順列関数を手動で書く必要があります。以下は、知的好奇心のためだけのものです。b -> c -> d->

では、2 番目と 4 番目の引数を交換するとどうなるでしょうか。[余談ですが、代数学の講義で覚えている定理があります。順列は、隣接する項目を交換する合成として作成できます。]

次のようにできます: ステップ 1: 2 を 4 の隣に移動します ( swap_2_3)

a1 -> a2 -> a3 -> a4 -> otherstuff
a1 -> a3 -> a2 -> a4 -> otherstuff

を使用してそこでそれらを交換しますswap_3_4

a1 -> a3 -> a2 -> a4 -> otherstuff
a1 -> a3 -> a4 -> a2 -> otherstuff

次に、swap_2_3再度使用して 4 を位置 2 に戻します。

a1 -> a3 -> a4 -> a2 -> otherstuff
a1 -> a4 -> a3 -> a2 -> otherstuff

それで

swap_2_4 = swap_2_3.swap_3_4.swap_2_3

たぶん、多くの結果とフリップで直接そこに到達するもっと簡潔な方法があるかもしれませんが、ランダムな混乱は私にとってそれを見つけられませんでした!

同様に、1 と 5 を交換するには、1 を 4 に移動し、5 と交換し、5 を 4 から 1 に戻します。

swap_1_5 = swap_1_2.swap_2_3.swap_3_4 . swap_4_5 . swap_3_4.swap_2_3.swap_1_2

または、端を裏返して再利用したい場合swap_2_4(1 を 2 に、5 を 4 に入れ替える)、swap_2_4 を端で再度裏返します。

swap_1_5' = swap_1_2.swap_4_5. swap_2_4 .swap_4_5.swap_1_2

もちろん、定義するのははるかに簡単です

swap_1_5'' f  a b c d e = f  e b c d a

これには、明確で、簡潔で、効率的であるという利点があり、明示的に注釈を付けなくても ghci に役立つ型シグネチャがあります。

しかし、これは素晴らしく面白い質問でした。ありがとう。

于 2012-08-26T16:39:34.160 に答える
12

一般的に最善の方法は、手動で行うことです。関数があると仮定します

f :: Arg1 -> Arg2 -> Arg3 -> Arg4 -> Res

そしてあなたが望む

g :: Arg4 -> Arg1 -> Arg3 -> Arg2 -> Res

それからあなたは書く

g x4 x1 x3 x2 = f x1 x2 x3 x4

特定の順列が複数回必要な場合は、もちろん、flip引数が 2 つの場合のように、それを抽象化できます。

myflip :: (a4 -> a1 -> a3 -> a2 -> r) -> a1 -> a2 -> a3 -> a4 -> r
myflip f x4 x1 x3 x2 = f x1 x2 x3 x4
于 2012-08-26T10:38:21.213 に答える