显示IO阵列

Die*_*lan 4 arrays io haskell

因为我一直在学习haskell,所以我很享受纯粹的部分,但现在我在monadic和IO部分遇到困难,可能会遇到一些人真正对这种语言感到愤怒的事情.我解决了一个项目euler问题,我简单地想要一个可变数组,因为我必须经常通过索引更新元素.我试过矢量但无法让它们工作,所以我尝试了Data.Array.IO.我可以读取和写入元素,但我不能按照我想要的方式在终端中显示数组.到目前为止,我有这个.

test = do
    arr <- newArray (1,10) 37 :: IO (IOArray Int Int)
    a <- readArray arr 1
    writeArray arr 1 64
    b <- readArray arr 1
    dispArray arr 
    return ()

dispArray arr = do
    (a,b) <- getBounds arr
    printf "["
    dispArray' arr a
    printf "]\n"
        where dispArray' arr i = do
                (a,b) <- getBounds arr
                if i < a || i > b
                    then return ()
                    else do
                        v <- readArray arr i
                        print v
                        dispArray' arr (i+1)
Run Code Online (Sandbox Code Playgroud)

正如您所料,这个输出是这样的:

[64
37
37
37
37
37
37
37
37
37
]
Run Code Online (Sandbox Code Playgroud)

但这很不方便,我希望[64,37,37,37....这样.我见过类似的函数toList,但我不想这样.我不希望每次显示时都转换为列表.所以我想我需要使用printf.所以我换成print vprintf " %s," (show v).但这不编译.我不知道为什么.我认为它会因为print :: Show a => a -> IO ()show :: Show a => a -> String那么,为什么不是工作,因为%s标志着一个字符串?所以我接着打电话给对方.看看printf是否会起作用.

printf " %s," "hello"
print v
Run Code Online (Sandbox Code Playgroud)

编译和显示:

[ hello,64
 hello,37
 hello,37
 hello,37
 hello,37
 hello,37
 hello,37
 hello,37
 hello,37
 hello,37
]
Run Code Online (Sandbox Code Playgroud)

为什么我不能使用show v?为什么haskell IO对初学者如此愤怒?

Gab*_*lez 6

你想要的咒语是:

putStr (show v)
Run Code Online (Sandbox Code Playgroud)

打印出来v没有换行符.

  • 除非你让你的例子更容易编译,否则很难说,但我的猜测是你被单态限制所困扰,因为你没有提供显式类型签名.你的错误与Haskell`IO`无关.GHC只是无法推断出什么类型的'printf`应该是因为它滥用类型类魔术很多. (3认同)

kos*_*kus 6

这是一个有趣的类型检查难题.

printf生成调用的错误消息是

Could not deduce (PrintfType (m a0))
  arising from the ambiguity check for `dispArray'
Run Code Online (Sandbox Code Playgroud)

短语Could not deduceambiguity通常暗示了一个事实,即GHC没有足够的类型信息,以完成这个程序应该如何打字.这可能是一个真正的类型错误,但也可以通过提供更多的类型信息来修复它(这就是这里的情况).

这里的罪魁祸首是printf,与可变阵列接口的灵活性相结合,而不是Haskell的IO系统.类型printf是一个巧妙的黑客,但仍然是一个黑客.为了知道仅依赖于格式字符串的各种类型的灵活数量的参数,printf具有不安全且信息量非常大的类型:

printf :: PrintfType r => String -> r
Run Code Online (Sandbox Code Playgroud)

所以我们真正知道的是第一个参数是类型的String.其余的可以是r类型类中的任何类型PrintfType.

实例的细节无关紧要.有趣的是show产生一个String,如果我们应用于printf格式字符串然后show生成第二个字符串,我们仍然留下一个相当无信息的类型:

> :t printf "%s," (show 2)
printf "%s," (show 2) :: PrintfType t => t
Run Code Online (Sandbox Code Playgroud)

特别是,这里没有迹象表明结果是在IOmonad中.

如果GHC可以从您所处的背景中得出结论,这通常不会成为问题IO.但内dispArray',您呼叫的唯一的其他功能readArray,getBounds,return(和dispArray'递归).这些函数都没有指定它存在于IO任何一个中.特别是,所有数组函数都在monad上重载,例如:

getBounds :: (Ix i, MArray a e m) => a i e -> m (i, i)
Run Code Online (Sandbox Code Playgroud)

(事实上​​,getBounds例如,也可以在STmonad环境中工作.)因此,没有任何东西dispArray'可以决定你的生活IO.而这又意味着GHC无法解决这种类型printf.

正如我所说,这是所需灵活性的结果printf,printf它本身无法提供这些信息,而且必须在外部提供.

解决方案很简单.正如其中一条评论中所建议的那样,将调用的结果类型注释为printf:

printf "%s," (show v) :: IO ()
Run Code Online (Sandbox Code Playgroud)

printf无论如何你正在使用(如果你实际上只对十进制数组的数组感兴趣),你也可以使用:

printf "%d," v :: IO ()
Run Code Online (Sandbox Code Playgroud)

对于读者来说,为定义中的任何其他内容提供类型签名也是足够的(但不太清楚),dispArray'以便修复返回类型IO ().例如,您可以在表达式return ()then-branch中注释if:

return () :: IO ()
Run Code Online (Sandbox Code Playgroud)