【问题标题】:Create List of Strings in Haskell在 Haskell 中创建字符串列表
【发布时间】:2013-07-09 02:55:41
【问题描述】:

我正在从 Java 到 Haskell 朝圣。从广义上讲,我了解了 Haskell 背后的主要概念。阅读所有的教程和书籍“很有意义”,但我被困在从头开始编写自己的代码。

我想在文件系统上创建 1000 个文件名

"myfile_1.txt" ... "myfile_1000.txt"

每个都包含一些虚拟文本。

到目前为止,我已经解决了整个 IO 问题,并意识到我需要构建一个包含 1000 个元素的字符串列表。所以我有:

buildNamesList :: [] -> []
buildNamesList ???

一旦我有了 List,我就可以在每个元素上调用 writefile 方法。我想不通的是如何在字符串的末尾添加一个数字来获取每个文件名,因为我不能在 Haskell 中有一个 int i = 0, i ++ 构造。

我在这里有点不了解,希望得到一些指导,谢谢

【问题讨论】:

  • [] -> []?这没有任何意义,[] 不是类型而是类型构造函数。你的意思可能是[a]->[a],但这也不对。 — 一般来说,在我看来,您了解 Haskell 背后的主要概念。你不想“建立”一个列表,你只需定义它。您不会“在每个元素上调用 writefile 方法”,而是遍历一个列表(例如使用 mapM_)。你也不是真的“在字符串的末尾添加一些东西”,而是定义一个新的后缀字符串——尽管实际上,我们称之为添加到末尾。
  • 谢谢,这是我不明白的一点。现在我有了这个字符串列表,然后我该如何遍历它,在元素 n 处提取字符串,然后将其传递给另一个函数?

标签: haskell


【解决方案1】:

一种可能的解决方案:

buildNamesList = map buildName [1..1000]
  where buildName n = "myfile_" ++ show n ++ ".txt"

【讨论】:

  • 啊。我明白了,非常感谢。但是这怎么不需要使用双冒号 :: 运算符定义 buildNamesList 的类型呢?
  • “双冒号”引入了类型签名。类型签名应该写出来,但不是强制性的。这里应该是:buildNamesList :: [String],因为该函数提供了一个字符串列表。
  • 顺便说一句,似乎很多人相信强静态类型意味着在任何地方声明类型。但事实是,当然,标准的 Haskell 几乎从不需要这个。
  • 人们不习惯打字推理。但是,如果您没有为顶级声明提供类型签名,则 ghc 会发出警告。
  • @Nicolas 我从未见过。你必须说-Wall 什么的吗?
【解决方案2】:
import Control.Applicative

fileNames = ("myFile_"++) <$> (++".txt")  <$> show <$> [1..1000]

【讨论】:

    【解决方案3】:

    然后我如何遍历它,在元素 n 处提取字符串,然后将其传递给另一个函数?

    不!从低效的列表中“挑选”一些东西。您不想担心如何获取每个元素,然后对其进行处理。这在命令式语言中是必要的,因为它们对“排序操作”的含义没有适当的抽象——这只是语言中内置的一些神奇的东西。 Haskell 有much more well-specified, mathematically sound and type-safe magic for that;因此,您不需要循环等。

    您知道如何处理每个元素 (String -&gt; IO ()),并且您知道数据的来源 ([String])。你也知道最终会发生什么 (IO ())。因此,您要查找的组合子的类型为( String -&gt; IO() ) -&gt; [String] -&gt; IO (),但显然它并不真正依赖于字符串数据,所以让我们将其简化为(a -&gt; IO()) -&gt; [a] -&gt; IO()。你可以look that up on Hoogle,其中提供了一些垃圾mapM_forM_,两者都可以满足你的需求:

    mapM_ (\filename -> writeFile filename "bla") filenamesList
    

    forM_ filenamesList $ \filename ->
       writeFile filename "bla"
    

    【讨论】:

    • 谢谢!有道理,所以因为 writeFile 函数有两个参数,我们需要使用 lamda 函数 (\filename) 来“保存” filenamesList 的映射元素的值,并将其正确插入到 writeFile 的调用中?这是看待它的正确方法吗?再次感谢。
    • 是的。有经验的 Haskellers 可能会缩短一点:mapM_ (`writeFile` "bla") filenamesList(注意后面的勾号)。这称为运算符部分,是部分应用的情况。
    【解决方案4】:

    有时我认为foldrfor 循环有些相似。这有点像i++ 构造,在循环中应用i

    foldr (\i accum -> ("myfile_" ++ show i ++ ".txt") : accum) [] [1..1000]
    


    另一种方式可能是zipWith,它应用一个函数来组合两个列表:

    zipWith (\a b -> a ++ show b ++ ".txt") (repeat "myfile_") [1..1000] 
    

    zipWith ($) (repeat (("myfile_" ++) . (++ ".txt") . show)) [1..1000]
    


    这也是一个递归示例,应用为fileList "myfile_" ".txt" [1..1000]

    fileList _     _   []     = [] 
    fileList fName ext (x:xs) = (fName ++ show x ++ ext) : fileList fName ext xs
    

    【讨论】:

      猜你喜欢
      • 2015-05-08
      • 2014-03-14
      • 1970-01-01
      • 1970-01-01
      • 2020-04-21
      • 2016-02-13
      • 2011-05-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多