Haskell 有条件地附加到列表
Haskell append to a list conditionally
我有 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
如何在这种情况下使用
这是一个简短的 ghci 会话,可能会给您一些想法:
> :m + Control.Monad Data.Either
> partitionEithers <$> replicateM 3 readLn :: IO ([Int], [Bool])
Left 5
Right True
Left 7
([5,7],[True])
你第二个问题的答案是let是递归的;所以 let g = 1:g
中的两个 g
指的是同一个内存中对象。
您正在考虑可变变量:您 "initializing" l1,l2
到空列表,然后推理用更长的列表更新它们。这种设计在命令式编程中工作得很好,但在纯函数式编程中就不那么简单了,因为它涉及到变异。
现在,即使在纯函数式编程中,我们也有办法通过 monad 来模拟突变。比如这里曾经可以通过IORefs
或者StateT 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)
上面的好处是 value1
和 value2
甚至可以是不同的类型。前面的片段强制他们共享他们的类型:因为我们构建了一个对列表,每对必须具有相同的类型。新列表现在不是 [Either a b]
类型,其中 a
是要放入 l1
的值类型,b
是 l2
.
一旦你得到 [Either a b]
,你想将它分成 [a]
和 [b]
。正如@DanielWagner 在他的回答中建议的那样,您可以为此利用 partitionEithers
。
我有 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
如何在这种情况下使用
这是一个简短的 ghci 会话,可能会给您一些想法:
> :m + Control.Monad Data.Either
> partitionEithers <$> replicateM 3 readLn :: IO ([Int], [Bool])
Left 5
Right True
Left 7
([5,7],[True])
你第二个问题的答案是let是递归的;所以 let g = 1:g
中的两个 g
指的是同一个内存中对象。
您正在考虑可变变量:您 "initializing" l1,l2
到空列表,然后推理用更长的列表更新它们。这种设计在命令式编程中工作得很好,但在纯函数式编程中就不那么简单了,因为它涉及到变异。
现在,即使在纯函数式编程中,我们也有办法通过 monad 来模拟突变。比如这里曾经可以通过IORefs
或者StateT 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)
上面的好处是 value1
和 value2
甚至可以是不同的类型。前面的片段强制他们共享他们的类型:因为我们构建了一个对列表,每对必须具有相同的类型。新列表现在不是 [Either a b]
类型,其中 a
是要放入 l1
的值类型,b
是 l2
.
一旦你得到 [Either a b]
,你想将它分成 [a]
和 [b]
。正如@DanielWagner 在他的回答中建议的那样,您可以为此利用 partitionEithers
。