Haskell使用$而不是括号是无效的
我是 Haskell 的新手。我想学习 $ 的用法,我写了两个小函数,但是第二个不起作用,有人可以解释一下那个小片段有什么问题吗?如果我理解正确,$ 就像括号一样工作。提前致谢 :)
myButLast::[a]->a
myButLast l = l !! (length l-2)
--not working
myButLast::[a]->a
myButLast l = l !! $ length l-2
回答
这不解析的原因True && || False
与不解析相同:您使用了两个相邻的中缀运算符。编译器可能会用这样的东西做什么?
你可以这样写:
myButLast l = (l!!) $ length l-2
这意味着,将函数(l!!)
应用于length l-2
. 这是有效的,因为您可以部分应用任何 Haskell 函数,包括中缀运算符。但这反而违背了使用$
避免括号的意义......
在一个有点不相关的注释上:length
并且!!
是 Haskell 中的代码异味。特别!!
是既不安全又缓慢,使用它几乎从来都不是一个好主意。如果你真的需要这样的直接访问操作,你应该使用数组/向量(见下文),但为了至少安全,你可以更改签名:
myButLast :: [a] -> Maybe a
myButLast v = case length v of
l | l>=2 -> Just . (v!!) $ l-1
_ -> Nothing
请注意,在这种情况下,$
确实有意义,因为它允许我将 RHS 和 LHS(函数Just
和(v!!)
函数的组合)括起来。
同一件事的快速矢量版本:
import qualified Data.Vector.Generic as VG
myButLast :: VG.Vector v a => v a -> Maybe a
myButLast v = case VG.length v of
l | l>=2 -> Just . VG.unsafeIndex v $ l-1
_ -> Nothing
回答
如果我理解正确,$ 就像括号一样工作。
没有。($) :: (a -> b) -> a -> b
不是一些特殊的语法,它只是一个像 的运算符(+)
,(-)
或者你自己定义的运算符。
这样做的原因是因为infixr 0
优先级,这意味着该运算符的优先级最低,因此即使它们包含运算符,也会将左侧的元素分组在右侧,因为优先级会更高。
($)
本身实现为:
infixr 0 $
($) :: (a -> b) -> a -> b
($) f x = f x
因此它本质上是一个简单的函数应用程序,但运算符优先级让它看起来好像左右操作数有隐藏的括号。
您的表达式的问题在于它现在包含两个接一个的运算符,实际上:
myButLast l = l !! $ length l-2
所以这意味着解析器感到困惑。
您可以使用运算符分段并构造函数(l !!)
,然后使用运算符:
myButLast l = (l !!) $ length l-2
-- ↑ operator sectioning
但是最好不要在length
这里首先使用:通过使用 length 您将在列表上迭代两次:首先确定列表,然后获取最后一个索引。这是低效的,并且可能还会导致使用大量内存。
您可以通过以下方式简化此操作:
myButLast :: [a] -> a
myButLast (x:x2:xs) = go xs x x2
where go [] y _ = y
go (y2:ys) _ y = go ys y y2
myButLast _ = error "empty list"