Haskell有条件地附加到列表中

时间:2015-04-25 00:57:36

标签: list haskell concat

我有2个列表,我试图填写项目。从stdin读取时,根据读取的其中一个内容的值,我想附加到不同的列表中。例如,

import Control.Monad(replicateM)

main = do
    n <- getLine
    let l1 = [], l2 = []
        in replicateM (read n) (getLine >>= (\line ->
            case line of "Yes" ->
                -- do something with line
                -- and append value of that thing to l1
                          "No" ->
                -- do something else
                -- append this value to l2
            putStrLn line))

我意识到上面的代码有语法错误等等,但希望你能看到我想要的东西并提出建议。

这是我提出的答案

虽然我们在这里,有人可以解释为什么这给了我一个无限的列表:

let g = []
let g = 1:g
-- g now contains an infinite list of 1's

这是我最终提出的:

import Control.Monad(replicateM)
import Data.Either

getEither::[String] -> [Either Double Double]
getEither [] = []
getEither (line:rest) = let [n, h] = words line
                            fn = read f :: Double
                            e = case heist of "Yes" -> Left fn
                                              "No"  -> Right fn
                            in e : getEither rest

main = do
    n <- getLine
    lines <- replicateM (read n) getLine
    let tup = partitionEithers $ getEither lines :: ([Double], [Double])
    print tup

不确定在此实例中如何使用fmap

2 个答案:

答案 0 :(得分:3)

这是一个简短的ghci会话,可能会给你一些想法:

> :m + Control.Monad Data.Either
> partitionEithers <$> replicateM 3 readLn :: IO ([Int], [Bool])
Left 5
Right True
Left 7
([5,7],[True])

你的第二个问题的答案是,让我们递归;所以g中的两个let g = 1:g指的是相同的内存中对象。

答案 1 :(得分:3)

你正在思考可变变量:你是&#34;初始化&#34; l1,l2到空列表,然后推断使用更长的列表更新它们。这种设计在命令式编程中运行良好,但在纯函数式编程中并不那么简单,因为它涉及到变异。

现在,即使在纯函数式编程中,我们也可以通过monad来模拟变异。例如,曾经可以通过IORefsStateT IO在此处实现变异。但是,在这种情况下,解决任务将是一种不必要的复杂方式。

您想要附加数据以形成两个列表。你想使用replicateM,这很好。关键是replicateM只会构建一个列表,而不是两个。现在的问题是:我们如何创建一个容易分成两个的列表?

第一个丑陋的尝试是生成标记值列表,即对列表:

case line of
   "Yes" -> let value = ... in
            return ("for l1", value)
   "No"  -> let value = ... in
            return ("for l2", value)

这样做会使replicateM生成一个列表,例如

[("for l1", value1), ("for l1", value2), ("for l2", value3), ...]

然后我们可以分成两个列表。

对标签使用字符串看起来有点不雅,因为布尔值就足够了:

case line of
   "Yes" -> let value = ... in
            return (True, value)
   "No"  -> let value = ... in
            return (False, value)

更好的方法是使用Either a b类型:

case line of
   "Yes" -> let value1 = ... in
            return (Left value1)
   "No"  -> let value2 = ... in
            return (Right value2)

上述优点是value1value2甚至可以是不同的类型。之前的片段迫使他们分享他们的类型:因为我们构建了一对对,所以每对必须具有相同的类型。现在,新列表不是[Either a b]类型,其中a是要放在l1中的值的类型,bl2的值。

获得[Either a b]后,您希望将其拆分为[a][b]。正如@DanielWagner在他的回答中建议的那样,你可以利用partitionEithers