这是我的第一个Haskell程序。你会用更好的方式写些什么部分?
-- Multiplication table
-- Returns n*n multiplication table in base b
import Text.Printf
import Data.List
import Data.Char
-- Returns n*n multiplication table in base b
mulTable :: Int -> Int -> String
mulTable n b = foldl (++) (verticalHeader n b w) (map (line n b w) [0..n])
where
lo = 2* (logBase (fromIntegral b) (fromIntegral n))
w = 1+fromInteger (floor lo)
verticalHeader :: Int -> Int -> Int -> String
verticalHeader n b w = (foldl (++) tableHeader columnHeaders)
++ "\n"
++ minusSignLine
++ "\n"
where
tableHeader = replicate (w+2) ' '
columnHeaders = map (horizontalHeader b w) [0..n]
minusSignLine = concat ( replicate ((w+1)* (n+2)) "-" )
horizontalHeader :: Int -> Int -> Int -> String
horizontalHeader b w i = format i b w
line :: Int -> Int -> Int -> Int -> String
line n b w y = (foldl (++) ((format y b w) ++ "|" )
(map (element b w y) [0..n])) ++ "\n"
element :: Int -> Int -> Int -> Int -> String
element b w y x = format (y * x) b w
toBase :: Int -> Int -> [Int]
toBase b v = toBase' [] v where
toBase' a 0 = a
toBase' a v = toBase' (r:a) q where (q,r) = v `divMod` b
toAlphaDigits :: [Int] -> String
toAlphaDigits = map convert where
convert n | n < 10 = chr (n + ord '0')
| otherwise = chr (n + ord 'a' - 10)
format :: Int -> Int -> Int -> String
format v b w = concat spaces ++ digits ++ " "
where
digits = if v == 0
then "0"
else toAlphaDigits ( toBase b v )
l = length digits
spaceCount = if (l > w) then 0 else (w-l)
spaces = replicate spaceCount " "
答案 0 :(得分:11)
您不使用import Text.Printf
中的任何内容。
从风格上讲,你使用的括号多于必要的括号。 Haskellers倾向于在清除那些无关紧要的东西时发现代码更具可读性。而不是h x = f (g x)
之类的内容,请写h = f . g
。
这里真的不需要Int
; (Integral a) => a
应该这样做。
foldl (++) x xs
== concat $ x : xs
:我相信内置的concat
比您的实施更有效。
此外,当函数在第二个参数中是惰性时,您应该更喜欢foldr
,因为(++)
是 - 因为Haskell是惰性的,这会减少堆栈空间(并且也可以在无限列表上工作)。
此外,unwords
和unlines
分别是intercalate " "
和concat . map (++ "\n")
的快捷方式,即“与空格连接”和“与换行符连接(加上尾随换行符)”;你可以替换那些东西。
除非您使用大数字,否则w = length $ takeWhile (<= n) $ iterate (* b) 1
可能更快。或者,对于懒惰的程序员,请w = length $ toBase b n
。
concat ( (replicate ((w+1)* (n+2)) "-" )
== replicate ((w+1) * (n+2)) '-'
- 不确定你是怎么错过这一个的,你只是把它弄好了几行。
你也可以使用concat spaces
做同样的事情。但是,实际使用Text.Printf
导入并写printf "%*s " w digits
?
答案 1 :(得分:11)
以下是一些建议:
为了使计算的表格更明显,我将列表[0..n]
传递给line
函数,而不是传递n
。
我会进一步拆分水平和垂直轴的计算,以便它们作为参数传递给mulTable
而不是在那里计算。
Haskell是高阶的,几乎没有计算与乘法有关。因此,我会将mulTable
的名称更改为binopTable
,并将实际的乘法作为参数传递。
最后,个别数字的格式是重复的。为什么不将\x -> format x b w
作为参数传递,从而无需b
和w
?
我建议的更改的净效果是您构建了一个通用的高阶函数来为二元运算符创建表。它的类型就像
binopTable :: (i -> String) -> (i -> i -> i) -> [i] -> [i] -> String
你最终可以使用更多可重复使用的函数 - 例如,布尔真值表应该是小菜一碟。
高阶和可重用是Haskell方式。
答案 2 :(得分:5)
Norman Ramsey提供了出色的高级(设计)建议;以下是一些低级别的:
concat (replicate i "-")
。为什么不replicate i '-'
?[String] -> String
,然后找到concat
。现在去替换所有这些折叠。unlines
。实际上,这更适合您的需求。这太神奇了!所以
-- Returns n*n multiplication table in base b
mulTable :: Int -> Int -> String
mulTable n b =
变为
mulTable :: Int -> Int -> String
mulTable size base =
where
子句中的调用者范围内,在那里它可以使用呼叫者的变量,使您无需将所有内容传递给它。所以
line :: Int -> Int -> Int -> Int -> String
line n b w y =
concat
$ format y b w
: "|"
: map (element b w y) [0 .. n]
element :: Int -> Int -> Int -> Int -> String
element b w y x = format (y * x) b w
变为
line :: Int -> Int -> Int -> Int -> String
line n b w y =
concat
$ format y b w
: "|"
: map element [0 .. n]
where
element x = format (y * x) b w
line
移至mulTable
的{{1}}条款中; imho,你应该。
where
条款中的where
条款令人不安,那么我建议改变你的缩进习惯。我的建议是使用始终为2或总是4个空格的一致缩进。然后,您可以在任何地方轻松查看其他where
中where
所在的位置。确定下面是它的样子(还有一些其他风格的变化):
where
答案 3 :(得分:4)
0)添加一个主要功能:-)至少基本
import System.Environment (getArgs)
import Control.Monad (liftM)
main :: IO ()
main = do
args <- liftM (map read) $ getArgs
case args of
(n:b:_) -> putStrLn $ mulTable n b
_ -> putStrLn "usage: nntable n base"
1)使用ghc
运行runhaskell
或-Wall
;贯穿hlint
。
虽然hlint
在这里没有提出任何特别的建议(只有一些冗余括号),但ghc
会告诉您实际上这里并不需要Text.Printf
...
2)尝试使用base = 1或base = 0或base = -1
运行它答案 4 :(得分:2)
如果您想要多行注释,请使用:
{- A multiline
comment -}
另外,在您处理必须折叠的大型列表的情况下,永远不要使用foldl
,而是使用foldl'
。它的内存效率更高。
答案 5 :(得分:1)
简短的评论说明每个函数的作用,它的参数和返回值总是好的。我必须仔细阅读代码才能完全理解它。
有人会说,如果你这样做,可能不需要显式类型签名。这是一个美学问题,我对它没有强烈的意见。
一个小警告:如果你删除了类型签名,你将自动获得所提到的多态Integral
支持ephemient,但由于臭名昭着的“单态限制”,你仍然需要toAlphaDigits
左右的一个。“