Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Function 在Haskell中部分应用几个函数_Function_Haskell_Function Composition - Fatal编程技术网

Function 在Haskell中部分应用几个函数

Function 在Haskell中部分应用几个函数,function,haskell,function-composition,Function,Haskell,Function Composition,假设在Haskell中,我有一组函数都依赖于相同的参数类型: f :: Par -> a -> b g :: Par -> b -> c 当我编写更多仍然依赖于此参数类型的函数时,我可以执行以下操作 h :: Par -> a -> c h par = myg . myf where myf = f par myg = g par pure (\x y -> (x, y)) <*> f <*> g

假设在Haskell中,我有一组函数都依赖于相同的参数类型:

f :: Par -> a -> b
g :: Par -> b -> c
当我编写更多仍然依赖于此参数类型的函数时,我可以执行以下操作

h :: Par -> a -> c
h par = myg . myf
    where myf = f par
          myg = g par
pure (\x y -> (x, y)) <*> f <*> g
然而,我不得不一直写这些
where
行。问题是:这能避免吗

[编辑:我试图提供一个最小的示例来说明问题,但显然该示例太小,无法说明我想要的内容。在实际问题中,h当然不仅仅是f和g的组合。下面是一些实际代码:

有一些功能

apply :: ChamberLattice -> ChLatword -> ChLatWord
reduce :: ChamberLattice -> ChLatWord -> ChLatWord
我正在定义一个函数

chaseTurn :: ChamberLattice -> Turn -> Parity -> ChLatWord -> ChLatWord
chaseTurn cl Straight _ xs = xs
chaseTurn cl t parity xs = if ((turn parity xs) == t)
                           then case myApply xs of
                               (y1:y2:ys) -> (y1:y2:(myChaseTurn t parity ys))
                               ys -> ys
                           else myReduce xs
where myApply = apply cl
      myChaseTurn = chaseTurn cl
      myReduce = reduce cl
]

(这个问题基本上与
但是在那里我用了一些让人分心的不幸的词。)

在Haskell中,所有函数都接受一个输入参数。但有时,应用函数的返回值是一个新函数。然后,作为第一步,您可以在函数的返回值
f
g
周围放上括号,使其更加明确:

f :: Par -> (a -> b)
g :: Par -> (b -> c)
函数也是类型,因此我们可以任意决定将
a->b
别名为
φ
(φ而不是f)和
b->c
别名为
γ(γ而不是g)。(是的,当你的字母用完时,你会去拿希腊字母表!)

这意味着您可以将函数视为具有

f :: Par -> φ
g :: Par -> γ
这两个都是所谓的reader monad的自动实例,它也是一个(应用程序)函子。特别是,
(>)Par
,或者如果有帮助的话,
Par->
,是一个
应用程序的
实例。这意味着您可以使用
pure

作为第一次尝试,您可以编写如下内容

h :: Par -> a -> c
h par = myg . myf
    where myf = f par
          myg = g par
pure (\x y -> (x, y)) <*> f <*> g
请注意,函数是从右到左组合的,因此
x
a->b
)排在第一位,然后是
y
b->c

但是,您可以翻转
f
g
左右:

h = pure (\y x -> y . x) <*> g <*> f
h=pure(\yx->y.x)gf
该显式lambda表达式可简化为:

h = pure (.) <*> g <*> f
h=纯(.)gf
最后,您可以使用中缀
操作符,而不是编写
纯(.)

h = (.) <$> g <*> f
h=(.)gf

此函数具有类型<代码> PAL> A>C.<代码> .< /P> < p>您正在执行<代码> h PAR=F PAR。g par

很多,而且
par
的东西开始变得杂乱无章

你不能做
h=f。g
,因为
par
参数也必须传递

因此,您提出了一个高效的合成操作符,它将为您实现这一点:

-- (.) :: (b -> c) -> (a -> b) -> a -> c
(§) :: (par -> b -> c) -> (par -> a -> b) -> par -> a -> c
(§) f g par = f par . g par
现在您可以执行
h=f§g
。这个操作符可能是以前发明的

顺便提一下,部分应用的函数是。这意味着您可以:

(§) f g par = (do { fpar <- f; gpar <- g; return (fpar . gpar) }) par
并减少参数:

(§) = ( \f m1 m2 -> do { x1 <- m1; x2 <- m2; return (f x1 x2) } ) (.)

在这一点上,我们真的不需要给它一个特殊的名称,因为
liftM2(.)
已经很短了。

如果您可以稍微调整您的签名,您已经发现了
阅读器的用例。如果你有

f :: a -> Par -> b
g :: b -> Par -> c
您可以将它们重新定义为

import Control.Monad.Trans.Reader

f :: a -> Reader Par b
g :: b -> Reader Par c
然后可以使用普通Kleisli合成运算符定义
h

import Control.Monad

h :: a -> Reader Par c
h = f >=> g

(即使不更改签名,我认为您也可以编写
h=flip(flip f>=>flip g)

这可以使用隐式参数来完成(隐式参数不是纯粹的Haskell,而是ghc语言扩展,请参阅)

上面的代码就变成了

f :: (?p :: Par) => a -> b

g :: (?p :: Par) => b -> c

h :: (?p :: Par) => a -> c
h = g . f

我不明白你为什么需要<代码>这里的子句,你可以把它写为<代码> h PAR=F PAR。g par
,甚至更好的是
h=liftM2(.)f g
@WillemVanOnsem
h=(.)f g
?@MarkSeemann的答案是当我想到“我得到了这个东西,我需要传递给很多函数,然后结合它们的结果”时,我经常会想到的模式。有点像叉形连接。例如,
(&&&)p1p2
是我经常使用的一个。@StefanWitzel:另一个选择是,如果你有工作代码,并且你正在寻找更好的抽象,你可以在上提交一段代码。术语说明:这是部分应用程序,而不是部分评估。部分求值是一种优化技术,它在编译时在一定程度上运行代码,以减少运行时所需的工作量。这和超级编译有关。@WillNess谢谢:)只是在寻找下一个困惑的新手。:)非常感谢。直到我重建了“->”的单子结构,我才完全理解你的答案。它帮助我把类型“p->a”想象成“由p参数化的a”。
import Control.Monad

h :: a -> Reader Par c
h = f >=> g
f :: (?p :: Par) => a -> b

g :: (?p :: Par) => b -> c

h :: (?p :: Par) => a -> c
h = g . f