我对Haskell中的元组和列表有疑问。我知道如何将输入添加到元组特定次数。现在我想将元组添加到未知次数的列表中;由用户决定他们想要添加多少个元组。
如果我事先不知道X,如何将元组添加到列表中x次?
答案 0 :(得分:4)
你可能有很多意思。例如,如果您想要单个值的几个副本,则可以使用前奏中定义的replicate
:
replicate :: Int -> a -> [a]
replicate 0 x = []
replicate n | n < 0 = undefined
| otherwise = x : replicate (n-1) x
在ghci:
Prelude> replicate 4 ("Haskell", 2)
[("Haskell",2),("Haskell",2),("Haskell",2),("Haskell",2)]
或者,也许你真的想做一些IO来确定列表。然后一个简单的循环将会:
getListFromUser = do
putStrLn "keep going?"
s <- getLine
case s of
'y':_ -> do
putStrLn "enter a value"
v <- readLn
vs <- getListFromUser
return (v:vs)
_ -> return []
在ghci:
*Main> getListFromUser :: IO [(String, Int)]
keep going?
y
enter a value
("Haskell",2)
keep going?
y
enter a value
("Prolog",4)
keep going?
n
[("Haskell",2),("Prolog",4)]
当然,这是一个特别糟糕的用户界面 - 我相信你可以提出十几种方法来改进它!但至少应该通过以下模式:您可以使用[]
之类的值和:
之类的函数来构建列表。还有许多其他更高级别的函数用于构造和操作列表。
P.S。元组列表没有什么特别之处(与其他事物的列表相比);以上功能显示,从不提及它们。 =)
答案 1 :(得分:1)
抱歉,你不能 1 。元组和列表之间存在根本区别:
由于这些原因,你不能。如果它不知道,有多少元素适合元组,你就不能给它一个类型。
我猜你从用户那里获得的输入实际上是一个像"(1,2,3)"
这样的字符串。尝试直接将它作为一个列表,而不是之前使它成为一个元组。你可以使用模式匹配,但这是一个有点偷偷摸摸的方法。我只是从字符串中删除了打开和关闭的paranthesis并用括号替换它们 - 瞧它就成了一个列表。
tuplishToList :: String -> [Int]
tuplishToList str = read ('[' : tail (init str) ++ "]")
抱歉,我没有看到您的最新评论。你试图做的并不是那么困难。我将这些简单的函数用于我的任务:
words str
将str
拆分为以前用空格分隔的单词列表。输出是String
的列表。 警告:仅当元组中的字符串不包含空格时才有效。实施更好的解决方案留给读者作为练习。
map f lst
将f
应用于lst
的每个元素
read
是一个神奇的函数,它从String中生成数据类型。它只有在您之前知道输出应该是什么时才有效。如果您真的想了解其工作原理,请考虑为您的特定用例实施read
。
你走了:
tuplish2List :: String -> [(String,Int)]
tuplish2List str = map read (words str)
1 正如其他一些人可能指出的那样,有可能使用模板和其他黑客,但我不认为这是一个真正的解决方案。
答案 2 :(得分:0)
在进行函数式编程时,通常最好考虑操作的组合而不是单个步骤。因此,我们可以先将输入分为字符串列表,然后将每个字符串转换为元组,而不是像将元组一次一个地添加到列表中那样思考它。
假设每个元组都写在一行上,我们可以使用lines
拆分输入,然后使用read
来解析每个元组。为了使其适用于整个列表,我们使用map
。
main = do input <- getContents
let tuples = map read (lines input) :: [(String, Integer)]
print tuples
我们试一试。
$ runghc Tuples.hs
("Hello", 2)
("Haskell", 4)
在这里,我按 Ctrl + D 将EOF发送到程序,(或Windows上的 Ctrl + Z ),然后打印结果。
[("Hello",2),("Haskell",4)]
如果你想要更具互动性的东西,你可能需要做自己的递归。有关此示例,请参阅Daniel Wagner's answer。
答案 3 :(得分:0)
一个简单的解决方案是使用列表理解,如此(在GHCi中完成):
Prelude> let fstMap tuplist = [fst x | x <- tuplist]
Prelude> fstMap [("String1",1),("String2",2),("String3",3)]
["String1","String2","String3"]
Prelude> :t fstMap
fstMap :: [(t, b)] -> [t]
这适用于任意数量的元组 - 与用户想要使用的数量一样多。
要在代码中使用它,您只需编写:
fstMap :: Eq a => [(a,b)] -> [a]
fstMap tuplist = [fst x | x <- tuplist]
我给出的例子只是一种可能的解决方案。顾名思义,当然,你可以写:
fstMap' :: Eq a => [(a,b)] -> [a]
fstMap' = map fst
这是一个更简单的解决方案。
答案 4 :(得分:0)
我猜这个,因为这是一个班级,你一直在研究Haskell for&lt; 1周,你实际上不需要做任何输入/输出。那比你可能的要先进一点。所以:
正如其他人所说,map fst
将获取任意长度的元组列表,并返回第一个元素。你说你知道怎么做。细
但是元组如何首先进入列表?好吧,如果你有一个元组列表并想要添加另一个,(:)
就可以了。像这样:
oldList = [("first", 1), ("second", 2)]
newList = ("third", 2) : oldList
你可以随心所欲地多次这样做。如果您还没有元组列表,则列表为[]
。
这可以做你需要的一切吗?如果没有,具体是什么缺失?
Eq a => [(a, b)]
这不是函数的类型。它是元组列表的类型。只需让用户在提示符下键入yourFunctionName
后跟[ ("String1", val1), ("String2", val2), ... ("LastString", lastVal)]
。