尝试使用optparse-applicative在某些命令行中实现fizzBuzz。
import Options.Applicative
data Args = Args { firstDivisor :: Int,
secondDivisor :: Int,
upperBound :: Int }
fizzBuzz :: Args -> IO ()
fizzBuzz opts i
| i `mod` firstDivisor opts == 0 && i `mod` secondDivisor opts == 0 = "fizzBuzz"
| i `mod` firstDivisor opts == 0 = "fizz"
| i `mod` secondDivisor opts == 0 = "buzz"
| otherwise = show i
main :: IO ()
main = print fizzBuzz
我设置了三个命令行参数;两个除数(在fizzBuzz中通常是3和5),第三个是上限(通常是100),但是当我去编译时我得到一个错误说:
Couldn't match expected type ‘Int -> [Char]’
with actual type ‘IO ()’
The equation(s) for ‘fizzBuzz’ have two arguments,
but its type ‘Args -> IO ()’ has only one
我的主要目标是使用三个命令行args打印出fizzBuzz系列。根据我的理解,我不喜欢为fizzBuzz提供额外的参数。试图理解为什么'我'不会在这里工作。
更新:
我认为这段代码更接近,因为它使用getArgs
而不是optparse来处理命令行。还添加了一个列表以针对fizzBuzz
运行。
import System.Environment
main :: IO ()
main = do
[s1,s2,s3] <- getArgs
print m
m = [ fizzBuzz i| i <-[1..s3]]
fizzBuzz i
| i `mod` s1 == 0 && s1 `mod` s2 == 0 = "fizzBuzz"
| i `mod` s1 == 0 = "fizz"
| i `mod` s2 == 0 = "buzz"
| otherwise = show i
所以我的问题是我无法访问s1和s2变量以获得我的fizzBuzz。如何访问main范围之外的那些args?也许还有其他功能可以提供帮助吗?
答案 0 :(得分:1)
如何访问main范围之外的那些args?
你不能。这正是范围的重点:内部变量保持在内部,很好地封装,防止任何难以跟踪的数据依赖。
实现访问 然而,这不一定是最好的方法 - 你实际上创建了一个大的伪全局IO范围。请注意,这绝不像命令式语言中的全局变量那么危险,但至少如果你想在其他语境中使用 当然,正确的方法是将这些变量作为函数参数传递。很像你已经拥有它 - 我觉得很奇怪,你再次删除它。 所以,首先找出s1
s2
s3
目标的最简单方法是在范围内定义fizzbuzz
:< / p>
main :: IO ()
main = do
[s1,s2,s3] <- map read<$>getArgs -- more on this later
let fizzBuzz i
| i `mod` s1 == 0 && s1 `mod` s2 == 0 = "fizzBuzz"
| i `mod` s1 == 0 = "fizz"
| i `mod` s2 == 0 = "buzz"
| otherwise = show i
m = [fizzBuzz i| i <-[1..s3]]
print m
fizzBuzz
,这显然不是最佳的。fizzBuzz
的签名。它需要什么,它会产生什么?嗯,它需要所有这些除数。因此,给它一个Args
参数实际上是很有意义的。但它还需要数字i
,这只是Int
。至于结果......为什么会IO ()
? fizzBuzz是一个非常好的纯函数,产生一个字符串。所以......
嗯,现在你很好。 定义实际上看起来完全正确。 (但一般来说,首先获得正确的类型签名更为明智,然后担心如何实际实现它。)fizzBuzz :: Args -> Int -> String
现在你只需要调用这个函数。在编写任何main
函数之前,我先在GHCi中使用它:
$ ghci FizzBuzz.hs
GHCi, version 7.10.2: http://www.haskell.org/ghc/ :? for help
[1 of 1] Compiling Main ( FizzBuzz.hs, interpreted )
Ok, modules loaded: Main.
*Main> fizzBuzz (Args 3 4 9) 3
"fizz"
*Main> fizzBuzz (Args 3 4 9) 5
"5"
...
如果您认为它正常,您可以将其包装到实际程序中。
请注意,Args
数据结构与OptParse
库实际上没有任何关系。该库对于使用适当的命令行程序是有意义的,但这在这里是有点过分的。然而,正如我已经展示的那样,使用Args
类型将这些除数传递给函数是很有意义的。
我现在只使用getArgs
来获取参数,并手动将它们包装到Args
结构中:
module Main where
import System.Environment (getArgs)
main :: IO ()
main = do
[s₁,s₂,s₃] <- getArgs
let divisors = Args (read s₁) (read s₂) (read s₃)
m = [fizzBuzz divisors i | i<-[1..read s₃]]
print m
你可能想知道read
的所有内容是什么。好吧,getArgs
为你提供了来自shell的命令行参数:as strings。但是你需要解释,即读取这些字符串为整数。请注意read
非常不安全:如果输入不是有效的整数文字,程序将崩溃。对你来说可能不是问题,但这是optparse
很好地为你解决的问题之一。
由于您根本不需要字符串形式的参数,因此您甚至可以在匹配各个参数之前read
所有:
main :: IO ()
main = do
[s₁,s₂,s₃] <- map read <$> getArgs
let divisors = Args s₁ s₂ s₃
m = [fizzBuzz divisors i | i<-[1..s₃]]
print m