如果我想更改函数中参数的顺序怎么办?
有flip
:
flip :: (a -> b -> c) -> b -> a -> c
但我不知道如何使它适用于更多的参数。有没有一种通用的方法来置换论点?
如果我想更改函数中参数的顺序怎么办?
有flip
:
flip :: (a -> b -> c) -> b -> a -> c
但我不知道如何使它适用于更多的参数。有没有一种通用的方法来置换论点?
如果您想在编写函数后对其进行编辑,那么您真的应该阅读 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)
我想交换前两个,我只是flip use3
按照你说的那样使用,但是如果我想交换第二个和第三个,我想要的是应用于flip
应用use3
到它的第一个参数的结果。
use3' :: Char -> Int -> Double -> String
use3' = (result) flip use3
让我们继续并交换use5
使用 5 的函数的第四个和第五个参数。
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
...如果您喜欢简约和优雅,那就应该停下来。请注意,由于出色的 Curry 和 的右关联性,该类型other
可能如此,swap_2_3 适用于接受任意数量以上两个参数的函数。对于更复杂的事情,你真的应该手动编写一个置换函数。以下内容只是为了求知欲。b -> c -> d
->
现在,交换第二个和第四个参数怎么样?[旁白:我记得在我的代数课上有个定理,任何排列都可以作为交换相邻项目的组合。]
我们可以这样做:第 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
然后再次使用以下命令将 4 交换回位置 2 swap_2_3
:
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 中具有有用的类型签名,而无需显式注释。
然而,这是一个非常有趣的问题,谢谢。
一般来说,最好的方法是手动完成。假设你有一个函数
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