Haskell 替换字符串中字符的更好方法

时间:2021-05-06 19:10:50

标签: list haskell

我做了这个功能

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 符号,但它有效,我想知道这里发生了什么

2 个答案:

答案 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]) 的函数类型
  • 接受两个参数(CharChar)并返回一个函数([Char] -> [Char])的函数类型
  • 采用三个参数(CharChar[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 作为输入并将其映射到箭头右侧的部分 (->)。因此,这意味着它将 Character c 映射到 b if c == a;否则返回 c