提问人:Enlico 提问时间:4/4/2023 更新时间:4/5/2023 访问量:87
将 m 元函数和 n 元函数组合在一个 (m+n) 函数中,返回它们的结果对
Combine m-ary function with n-ary function in a single (m+n)-ary function returning the pair of their results
问:
我不知道这个应用程序会有多大用处,但我对它感到好奇,因为这个C++回答了我的一个问题。
因此,给定,例如,三元和二进制,例如f
g
f x y z = x + 10*y + 100*z
g x y = x + 10*y
我怎样才能得到一个函数,以便以下内容成立?h
h f g 1 2 3 4 5 == (321, 54)
显然我可以定义
h f g = \x y z v w -> (f x y z, g v w)
但是我很好奇,是否可以使用一些现有的抽象,以无点的方式为每个 arities m
和 n
完成。
对于此特定示例 (),pointfree.io 显示结果变得不可读:m == 3 && n == 2
h = flip . ((flip . ((flip . (((.) . (.) . (,)) .)) .)) .)
但我仍然很好奇是否存在其他东西。
为了好玩,我尝试在机械上应用组合逻辑来推导一个公式,但仍然仅适用于以下特定情况:m == 3 && n == 2
b = (.) -- bluebird (names from "To Mock a Mockingbird")
c = flip -- cardinal (names from "To Mock a Mockingbird")
p = (,)
f x y z = x + 10*y + 100*z
g x y = x + 10*y
{-
p(fxyz)(gvw) = ?xyzvw, with ? in terms of p, f, g and known birds
(p(fxyz))(gvw)
B(B(p(fxyz)))gvw
CBg(B(p(fxyz)))vw
B(CBg)B(p(fxyz))vw
B(B(CBg)B)p(fxyz)vw
B(B(B(CBg)B)p)(fxy)zvw
B(B(B(B(CBg)B)p))(fx)yzvw
B(B(B(B(B(CBg)B)p)))fxyzvw
B(B(B(B(B(CBB)(CB)g)p)))fxyzvw
B(B(B(BB(B(CBB)(CB))gp)))fxyzvw
B(B(BB(BB(B(CBB)(CB))g)p))fxyzvw
B(B(B(BB)(BB(B(CBB)(CB)))gp))fxyzvw
B(BB(B(BB)(BB(B(CBB)(CB)))g)p)fxyzvw
BB(BB(B(BB)(BB(B(CBB)(CB)))g))pfxyzvw
B(BB)(B(BB)(B(BB)(BB(B(CBB)(CB)))))gpfxyzvw
C(C(B(BB)(B(BB)(B(BB)(BB(B(CBB)(CB))))))p)fgxyzvw
-}
h = c (c (b (b b) (b (b b) (b (b b) (b b (b (c b b) (c b)))))) p)
h f g 1 2 3 4 5 == (321, 54) -- True
答:
正如 Willem Van Onsem 在评论中指出的那样,您的问题的问题在于,从技术上讲,Haskell 中的每个函数都只接受一个参数。我知道你的意思,我知道你想做什么,但没有理由不能只是一个返回函数的单参数函数,而不是一个双参数函数。事实上,如果我们为类似的东西定义一个实例,那么它完全有可能是一个 3 参数函数!(+)
Num
Int -> Int
(+)
另一方面,仅仅因为无法推断出参数的数量,并不意味着你注定要失败。如果你愿意给 GHC 一些提示,你可以在类型级别做你想做的事。请考虑以下几点:
data Nat = Zero | Succ Nat
type One = Succ Zero
type Two = Succ One
type Three = Succ Two
type Four = Succ Three
class NFun m n f g where
type family FunT m n f g
h :: f -> g -> FunT m n f g
instance NFun m n x y => NFun (Succ m) n (a -> x) y where
type FunT (Succ m) n (a -> x) y = a -> FunT m n x y
h f g a = h @m @n (f a) g
instance NFun Zero n x y => NFun Zero (Succ n) x (a -> y) where
type FunT Zero (Succ n) x (a -> y) = a -> FunT Zero n x y
h f g a = h @Zero @n f (g a)
instance NFun Zero Zero x y where
type FunT Zero Zero x y = (x,y)
h = (,)
(使用 GHC 的 TypeLits 可能可以做得更漂亮,但我总是在让归纳法计算出来时遇到一些麻烦,所以我自己滚动了自然数。
在这里,我定义了一个类型类,它采用两个数值类型,指示函数将采用多少个参数。第一个实例为第一个函数提供参数,第二个实例为第二个函数提供参数。最后一个实例将结果配对。NFun
你可以像这样使用它:
f x y z = x + 10*y + 100*z
g x y = x + 10*y
h @Three @Two f g 1 2 3 4 5 == (321, 54)
评论
(+)
Num a => a -> a -> a
instance Num ((->) b b)
(+) :: (b -> b) -> (b -> b) -> (b -> b)
(+) :: (b -> b) -> (b -> b) -> b -> b
instance Num (a -> Integer) where (+) = liftA2 (+); fromInteger = const
3 + 4
7
(+)
除非函数是具体类型,否则函数的 arity(如果我们省略严格来说每个函数的 arity 为 1)可能是可变的。事实上,一个很好的例子是 printf :: PrintfType r => String -> r
。
这似乎是一个简单的功能。但是有一个问题:这里可以是 的任何实例,我们有作为实例:r
PrintfType
instance PrintfType a ~ () => PrintfType (IO a) where
-- ...
instance IsChar c => PrintfType [c] where
-- ...
instance (PrintfArg a, PrintfType r) => PrintfType (a -> r) where
-- ...
前两个并不那么有趣。最后一个是。实际上,这意味着它可以返回另一个函数。
这意味着可以用作:printf
printf "foo" :: String
printf "foo %i" 14 :: String
printf "foo %i %i" 14 25 :: String
他们实现了某种可变参数函数,其中参数的数量取决于您如何使用 .printf
虽然我并不完全满意,因为与许多其他 Haskell 函数相比,它不是很“安全”,但它演示了如何制作可调参数函数。例如,可以传递太多或太少的参数,并且不能保证检索整数或类似数字的值,因此有更好的方法来格式化字符串。printf
%i
但无论如何,参数的数量并不取决于函数本身,因此您不能从函数的角度推导出来,而是从它的使用方式中得出。因此,除非类型在函数中都是具体的,否则如果使用类型类,则不再容易确定函数的 arity,从而将两个函数“合并”在一起。
评论
printf "foo %s %s bar" "qux"
%s
printf "foo" 42
%i
下一个:咖喱可以通过协程来实现吗?
评论
printf
String
IO String
uncurry
h1 &&& h2
curry