我做了这个功能
replace :: Char -> Char -> [Char] -> [Char]
replace _ _ [] = []
replace a b (c:r)
| a == c = b : replace a b r
| otherwise = c : replace a b r
当我发现这个时,我正在寻找一种更好的方式来编写它:
replace :: Char -> Char -> [Char] -> [Char]
replace a b = map $ \c -> if c == a then b else c
甚至没有写第三个参数,我不理解 $ 和 \c 符号,但它有效,我想知道这里发生了什么
答案 0 :(得分:7)
$
运算符将函数应用于参数。我们可以将您的示例重写为
replace :: Char -> Char -> [Char] -> [Char]
replace a b = map (\c -> if c == a then b else c)
在实际中,Haskeller 经常使用 $
来避免括号。由于其他原因,它很少使用。
关于 \c -> ...
:这是一个匿名函数,也称为“lambda”。它代表以 c
作为参数并返回 ...
部分的函数。在您的情况下,该函数采用 c
并检查它是否等于 a
,在这种情况下,它返回 b
,否则返回 c
。我们可以在没有 lambda 的情况下重写代码如下:
replace :: Char -> Char -> [Char] -> [Char]
replace a b = map myFun
where
myFun :: Char -> Char
myFun = \c -> if c == a then b else c
或者,将参数 c
移动到 =
的左侧,如下所示:
replace :: Char -> Char -> [Char] -> [Char]
replace a b = map myFun
where
myFun :: Char -> Char
myFun c = if c == a then b else c
关于“缺少第三个参数”:可以通过多种方式读取类型Char -> Char -> [Char] -> [Char]
:
Char
) 并返回一个函数 (Char -> [Char] -> [Char]
) 的函数类型Char
和 Char
)并返回一个函数([Char] -> [Char]
)的函数类型Char
、Char
和 [Char]
)并返回一个列表 ([Char]
) 的函数类型由于柯里化,所有这三种解释都是兼容的。确实,“两个参数”函数
foo :: A -> B -> B
foo x y = y
和函数
foo :: A -> B -> B
foo x = id -- id is the identity function B -> B
都一样。
在您的示例中,如果需要,您可以将缺失的参数添加到 =
的两侧,如下所示:
replace :: Char -> Char -> [Char] -> [Char]
replace a b xs = map myFun xs
where
myFun :: Char -> Char
myFun c = if c == a then b else c
在这段展开后的代码中,可以看到map myFun xs
使用map
(库函数)将myFun
应用于列表xs
的所有元素,并返回列表所有的结果。
这有效地实现了您想要的替换。
然而,没有添加第三个参数,
replace :: Char -> Char -> [Char] -> [Char]
replace a b = map myFun
where ...
我们仍然可以将 map myFun
解释为将函数 myFun :: Char -> Char
转换为函数 [Char] -> [Char]
。如果我们将其解释为“两个参数”函数,后者确实是 replace
的返回类型。也就是说,replace 'a' 'b'
是函数 [Char] -> [Char]
,它接受一个字符串并将其中的每个 'a'
替换为一个 'b'
。
答案 1 :(得分:5)
甚至没有写第三个参数。 (…)
在 Haskell 中,所有函数都只有 一个 参数。实际上 Char -> Char -> [Char] -> [Char]
是 Char -> (Char -> ([Char] -> [Char]))
的缩写,因此它是一个函数,它接受一个 Char
然后返回另一个函数。如果我们将该函数与另一个 Char
一起应用,它会返回一个 [Char] -> [Char]
类型的函数,该函数会将 Char
的列表映射到 Char
的列表,最后如果我们使用 Char
的列表调用该函数,我们得到 Char
的列表。
这意味着如果函数头部有两个参数,那么 map $ \c -> if c == a then b else c
应该返回一个函数,就是这种情况。
(...) 我看不懂 $
(...) 符号。
($) :: (a -> b) -> a -> b
函数定义为:
infixr 0 $
($) :: (a -> b) -> a -> b
($) f x = f x
因此它是函数应用程序。使用它的原因是因为它具有 0
的优先级,这意味着 a $ b x
例如将被评估为 a (b x)
,而 a b x
将被评估如(a b) x
。因此,这意味着该函数等价于 map (\c -> if c == a then b else c)
。
我不明白 (...) \c 符号
那是一个 lambda 表达式。该函数将变量 c
作为输入并将其映射到箭头右侧的部分 (->
)。因此,这意味着它将 Char
acter c
映射到 b
if c == a
;否则返回 c
。