【问题标题】:How do I add x tuples into a list x number of times?如何将 x 个元组添加到列表中 x 次?
【发布时间】:2011-12-04 16:13:32
【问题描述】:

我对 Haskell 中的元组和列表有疑问。我知道如何将输入添加到元组中特定次数。现在我想将元组添加到列表中的次数未知;由用户决定他们要添加多少元组。

当我事先不知道 X 时,如何将元组添加到列表中 x 次?

【问题讨论】:

  • 您能否尝试更具体地说明“将元组添加到列表中”的含义?你从哪里得到这些元组?
  • 您能否举一个简单的例子,您想表达什么?我知道如何将输入添加到一个元组中特定的次数。«。目前尚不清楚您是否希望 (a,b) (c,d) 成为 [a,b,c,d][(a,b),(c,d)]
  • 我想我现在在这里看到了更多你想要的东西。但。您链接到的类型不是您想要的功能的类型。它可能是整个问题的类型,但不是您需要帮助的子部分的类型。
  • 这些元组是从哪里来的?它们在文件中吗?用户在命令行输入的?还是什么?
  • 回答后不要删除您的问题,为社区保留。

标签: list haskell tuples


【解决方案1】:

抱歉,您不能1。元组和列表之间存在根本区别:

  • 元组总是有有限数量的元素,这在编译时是已知的。具有不同数量元素的元组实际上是不同的类型。
  • 列出一个有尽可能多的元素。不需要在编译时知道列表中元素的数量。
  • 元组可以包含任意类型的元素。由于您可以使用元组的方式始终确保没有类型不匹配,因此这是安全的。
  • 另一方面,列表的所有元素都必须具有相同的类型。 Haskell 是一种静态类型语言;这基本上意味着所有类型在编译时都是已知的。

由于这些原因,您不能。如果不知道有多少元素可以放入元组,则不能给它一个类型。

我猜你从用户那里得到的输入实际上是一个像"(1,2,3)" 这样的字符串。尝试将其直接设为列表,而不是之前将其设为元组。您可以为此使用模式匹配,但这里有一个稍微偷偷摸摸的方法。我只是从字符串中删除了开始和结束括号并用括号替换它们 - 瞧,它变成了一个列表。

tuplishToList :: String -> [Int]
tuplishToList str = read ('[' : tail (init str) ++ "]")

编辑

抱歉,我没有看到您的最新评论。你尝试做的事情并不难。我使用这些简单的函数来完成我的任务:

  • words strstr 拆分为之前由空格分隔的单词列表。输出是Strings 的列表。 注意:仅当元组中的字符串不包含空格时才有效。实施更好的解决方案留给读者作为练习。

  • map f lstf 应用于lst 的每个元素

  • read 是一个神奇的函数,可以从字符串中生成数据类型。它只有在你之前知道输出应该是什么的情况下才有效。如果您真的想了解它是如何工作的,请考虑为您的特定用例实现 read

你来了:

tuplish2List :: String -> [(String,Int)]
tuplish2List str = map read (words str)

1正如其他一些人可能指出的那样,使用模板和其他技巧可能是可能的,但我认为这不是一个真正的解决方案。

【讨论】:

  • @user988050 在没有源代码的情况下很难猜测如何概括您的程序。考虑编辑问题以添加一些源代码。请用foru空格缩进源代码(或突出显示它并按{}按钮以更方便)
  • @user988050 考虑fst :: (a,b) -> a。您可以使用map fst 来获得您想要的。顺便提一句。我建议你阅读 Learn You A Haskell,因为它是最好的介绍。
【解决方案2】:

你可能有很多意思。例如,如果你想要单个值的几个副本,你可以使用 replicate,在 Prelude 中定义:

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)]

当然,这是一个特别糟糕的用户界面——我相信您可以想出十几种方法来改进它!但至少该模式应该会大放异彩:您可以使用 [] 之类的值和 : 之类的函数来构造列表。还有许多其他高级函数用于构造和操作列表。

附:元组列表没有什么特别之处(与其他事物的列表相比);上述功能通过从不提及它们来显示。 =)

【讨论】:

  • @user988050 如果这确实是您要编写的函数的类型,那么您的问题非常具有误导性。您应该在问题正文中打开一个具有该类型的新问题。另外,如果这是作业,请标记它。
  • 澄清一点:您正在描述一个函数,该函数接受一个元组(或多个,但暂时将其放在一边)并返回一个元组列表。那将有类型(a,b) -&gt; [(a,b)]。但是链接中的类型描述了一个函数,该函数接受一个元组列表,并返回一个第一个元素的列表。也许重读这个问题?
【解决方案3】:

在进行函数式编程时,通常最好考虑操作的组合而不是单个步骤。因此,我们可以先将输入分成一个字符串列表,然后将每个字符串转换为一个元组,而不是像一次将一个元组添加到一个列表中那样考虑它。

假设每个元组都写在一行上,我们可以使用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

【讨论】:

    【解决方案4】:

    一个简单的解决方案是使用列表推导,就像这样(在 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
    

    这是一个更简单的解决方案。

    【讨论】:

      【解决方案5】:

      我猜,因为这是一堂课,而且你已经学习 Haskell 不到 1 周,你实际上不需要做任何输入/输出。这比你可能要先进一点。所以:

      • 正如其他人所说,map fst 将采用任意长度的元组列表,并返回第一个元素。你说你知道怎么做。很好。

      • 但是元组首先是如何进入列表的呢?好吧,如果您有一个元组列表并想添加另一个,(:) 就可以了。像这样:

        oldList = [("first", 1), ("second", 2)]
        newList = ("third", 2) : oldList
        

        您可以随意多次这样做。如果您还没有元组列表,您的列表是[]

      这能满足您的所有需求吗?如果没有,具体缺少什么?

      编辑:使用更正的类型:

      Eq a =&gt; [(a, b)]

      这不是函数的类型。它是元组列表的类型。只需让用户在提示符处输入yourFunctionName,然后输入[ ("String1", val1), ("String2", val2), ... ("LastString", lastVal)]

      【讨论】:

      • @user988050 - 您是否试图允许用户输入具有可变元素计数的元组(例如,(x, y),然后是 (x, y, z),然后是 (w, x, y, z))?因为如果是这样,这不能成为 Haskell 中的列表。 Haskell 是相当强类型的,并且确实允许列表由不同的类型组成(不同大小的元组是不同的类型)。
      • 你不需要一个特殊的函数来创建一个元组——你只需输入(x, y)。 (为了清楚起见,省略了技术。)如果您需要一堆它们,并且您处于交互式提示中,只需将它们全部输入到一个列表中,就像上面的编辑一样。
      猜你喜欢
      • 2014-04-25
      • 2013-03-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多