我想显示具有任意类型的列表的内容,每行一个元素,从1开始编号,如下所示:
字符串示例:
> bs "Hallo"
1. 'H'
2. 'a'
3. 'l'
4. 'l'
5. 'o'
Run Code Online (Sandbox Code Playgroud)
整数示例
> bs [5,6,1,2]
1. 5
2. 6
3. 1
4. 2
Run Code Online (Sandbox Code Playgroud)
元组示例
> bs [(4,"Test"),(3,"Aye"),(5,"Fives")]
1. (4,"Test")
2. (3,"Ayes")
3. (4,"Fives)
Run Code Online (Sandbox Code Playgroud)
我发现这是一个解决方案:
bs' :: Show a => [a] -> Integer -> IO ()
bs' [] _ = return ()
bs' (x:xs) y = do
putStrLn $ (show y) ++ ". " ++ (show x)
bs' xs $ succ y
bs x = bs' x 1
Run Code Online (Sandbox Code Playgroud)
因为我是Haskell的绝对初学者,我想知道解决这个问题的"最佳"方法是什么?我是在正确的道路上还是只是简单的"坏"Haskell.
如何在没有''的String示例中输出Chars,并且仍然能够输出任何具有Show实例的类型?
我想从不同的角度了解解决此任务的其他方法,例如:可读性,效率,代码重用.
我也是这样做的,发现它甚至更奇怪(但不知何故很酷):
bs' :: Show a => [(Integer,a)] -> IO ()
bs' [] = return ()
bs' ((x1,x2):xs) = do
putStrLn $ (show x1) ++ ". " ++ (show x2)
bs' xs
bs x = bs' (zip [1..] x)
Run Code Online (Sandbox Code Playgroud)
我已经完成了大约25年的命令式编程,并且真正对学习新东西感兴趣.同时如果对Haskell中的代码感到难以置信的"奇怪",我仍然无法想象这个"疯狂的语言来自月球"如何完成一个大项目:)
编辑:我要感谢大家.我选择一个答案,因为我必须,但所有人都非常有帮助!我还想说我的顶级解决方案是因为"在真正的问题中"来自我必须跳过一些列表元素并且使用zip方法时编号出错了.在阅读完所有答案之后,我很确定,即使这样,解决方案仍然是首先过滤列表然后压缩映射输出函数.
还有Text.Printf,如果你怀念printf:
import Text.Printf
bs :: Show a => [a] -> IO ()
bs = sequence_ . zipWith (\n x -> printf "%d. %s\n" n (show x)) [(1 :: Int)..]
Run Code Online (Sandbox Code Playgroud)
或者,如果您不想使用printf:
bs xs = sequence_ $ zipWith (\n x -> mapM_ putStr [show n, ". ", show x, "\n"]) [1..] xs
Run Code Online (Sandbox Code Playgroud)
这些都不是非常惯用的,我想大多数人会创建一个返回字符串然后在必要时打印的纯函数:
bs' xs = unlines $ zipWith (\n x -> show n ++ ". " ++ show x) [1..] xs
bs xs = putStr (bs' xs)
Run Code Online (Sandbox Code Playgroud)
将您的任务分解为小部分是很好的。在这种情况下,您需要 1) 通过显示元素并在前面添加一个数字来渲染每一行,然后 2. 在终端中将每个渲染打印在其自己的行上。
所以渲染只是一些字符串修改:
renderLine :: Show a => Integer -> a -> String
renderLine i a = show i ++ ". " ++ show a
Run Code Online (Sandbox Code Playgroud)
并且多行的组合需要将连续的数字传递到渲染中:
bs :: Show a => [a] -> String
bs = unlines . zipWith renderLine [1..]
Run Code Online (Sandbox Code Playgroud)
这给我们带来了如下结果:
*Main> putStr $ bs "Hello"
1. 'H'
2. 'e'
3. 'l'
4. 'l'
5. 'o'
*Main> putStr $ bs [1,2,3,4]
1. 1
2. 2
3. 3
4. 4
*Main> putStr $ bs [(4,"Test"),(3,"Aye"),(5,"Fives")]
1. (4,"Test")
2. (3,"Aye")
3. (5,"Fives")
Run Code Online (Sandbox Code Playgroud)
问题
由于我是 Haskell 的绝对初学者,我想知道解决这个问题的“最佳”方法是什么?我走的路是对的还是那只是简单的“坏”Haskell。
我想说,最好的方法是对于熟练的 Haskell 程序员来说最容易阅读的方法,这通常意味着使用 Prelude 中的常用函数,例如zipWith,并尽可能避免手动原始递归。
如何在不带 '' 的情况下输出 String 示例中的字符,并且仍然能够输出具有 Show 实例的任何类型?
要使用相同的函数对不同类型执行不同的操作,您需要一个类型类。
编辑我读得不够仔细。我现在看到您想让这项工作适用于任何Show.
这里可以(并且可能会)给出许多长答案,说明如何以Char一种方式表现,然后有效地提升所有其他Show实例,留下一些编译器必须解决的歧义。{-# LANGUAGE ... #-}我将跳过这一点,只是告诉您我们需要通过下面看到的编译指示对该语言进行一些扩展:
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE OverlappingInstances #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE IncoherentInstances #-}
renderLine :: BS a => Integer -> a -> String
renderLine i a = show i ++ ". " ++ r a
bs :: BS a => [a] -> String
bs = unlines . zipWith renderLine [1..]
class BS a where
r :: a -> String
instance BS Char where
r c = [c]
instance (Show a) => BS a where
r = show
Run Code Online (Sandbox Code Playgroud)
在实践中:
*Main> putStr $ bs [(4,"Test"),(3,"Aye"),(5,"Fives")]
1. (4,"Test")
2. (3,"Aye")
3. (5,"Fives")
*Main> putStr $ bs "Hello"
1. H
2. e
3. l
4. l
5. o
Run Code Online (Sandbox Code Playgroud)