如何改变参数的顺序?

时间:2012-08-26 09:33:33

标签: haskell

如果我想更改函数中参数的顺序怎么办?

flip

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

但我不知道如何让它适用于更多的参数。是否有一种通用方法来置换参数?

2 个答案:

答案 0 :(得分:36)

如果您想在编写后编辑功能,那么您真的应该阅读 Conal Elliott的优秀博客文章语义编辑器组合

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

事实上,无论如何,每个人都应该阅读它。这真的很有用 方法(我在这里滥用)。 Conal使用的构造不仅仅是resultflip,而且非常灵活。

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

假设我有一个使用3个参数的函数

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

我想换掉前两个,我只是按你说的flip use3, 但如果我想交换第二个和第三个,我想要的是应用flipuse3应用于第一个参数的结果。

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

让我们继续并交换使用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应用于将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

......如果您喜欢简约和优雅,那就应该停下来。 请注意,other类型可能是b -> c -> d因此,因为美妙的咖喱和->的正确关联性, swap_2_3适用于一个函数,它接受两个以上的任意数量的参数。 对于任何更复杂的事情,你应该亲自编写一个置换函数。 以下是为了求知欲。

现在,如何交换第二个和第四个参数? [旁白:从我的代数讲座中我记得有一个定理 任何排列都可以作为交换相邻项目的组合。]

我们可以这样做: 第1步:在4(swap_2_3

旁边移动2
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交换,从4移动5到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中具有有用的类型签名,而无需明确注释它。

然而,这是一个非常有趣的问题,谢谢。

答案 1 :(得分:12)

一般来说,最好的方法就是手动完成。假设你有一个功能

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

你想要

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

然后你写

g x4 x1 x3 x2 = f x1 x2 x3 x4

如果你需要多次特定的排列,那么你当然可以从中抽象出来,就像flip对于两个论证的情况一样:

myflip :: (a4 -> a1 -> a3 -> a2 -> r) -> a1 -> a2 -> a3 -> a4 -> r
myflip f x4 x1 x3 x2 = f x1 x2 x3 x4