Haskell: 在函数内部的 do 块之后使用多个 let 和 return 一个值

Haskell: Using multiple let and return a value after the do block inside a function

正如标题所说,我想 return 在 do 块之后的一个值。

示例:编写一个在数组中的给定位置插入变量的函数:

insertAt :: a -> Int -> [a] -> [a]
insertAt x n xs = do
            let before = take n xs
            let after  = drop n xs
            let merged = before ++ [x] ++ after
            in  merged

例如:

insertAt 'x' 3 "Aleander" => "Alexander"

无论如何,当使用单个 let 调用时,可以 return 使用 in 关键字的值,但是let 的多次调用,如示例所示,我收到错误:

错误:输入“in”时出现解析错误

我知道我可以在一个 let 用法中完成所有事情,但我想知道如何处理多个 let 调用:)

感谢您的帮助!

不要使用do表达式。 do 表达式是具有绑定的表达式的语法糖。是的,列表是 Monad 的一个实例,但您没有以正确的方式使用它。

您可以在此处定义您的 let 块,例如:

insertAt :: a -> Int -> [a] -> [a]
insertAt x n xs =
    let before = take n xs
        after = drop n xs
        merged = before ++ [x] ++ after
    in  merged

但是使用splitAt :: Int -> [a] -> ([a], [a])可能更优雅,比如:

insertAt :: a -> Int -> [a] -> [a]
insertAt x n xs = let (hs,ts) = splitAt n xs in hs ++ x : ts

不过,如果你想使用列表 monad,你可以这样写

insertAt x n ys = do
   (i, y) <- zip [0..] ys
   if i == n then [x, y] else [y]

这取决于 "numbering" 输入列表的每个元素。通常,您只需 return 每个找到的元素(记住,列表 monad 中的 return x == [x] )。但是在位置n,你想在现有列表的当前元素之前"sneak in" x。您可以通过提供列表 [x,y] 而不是简单地 [y].

来做到这一点

insertAt 'x' 3 "Aleander" 本质上变成了 concat ["A", "l", "e", "xa", "n", "d", "e", "r"].


缺点:它不会将 'x' 附加到输出,无论您提供什么 n,因此这可能更像是列表 monad 的演示,而不是您实际的解决方案问题。一个简单的修复是针对特殊情况 n == 0,然后在 n 元素 之后插入 x 。这允许您附加 if n == length ys,而不是 n > length ys:

insertAt x n ys = if n == 0 then x:ys else do
    (i, y) <- zip [1..] ys  -- Note the increase in indices
    if i == n then [y, x] else [y]