Haskell:需要计算器程序启示

3

我有一个任务,需要在Haskell中创建一个计算器程序。例如,用户可以通过命令行使用计算器,例如:

>var cola =5; //define a random variable
>cola*2+1;
(print 11)
>var pepsi = 10 
>coca > pepsi;
(print false)
>def coke(x,y) = x+y;  //define a random function
>coke(cola,pepsi);
(print 15)

//实际上它比上面更加复杂

我不知道如何在Haskell中编程。我现在能想到的是将命令行读取为String,将其解析为一个标记数组。也许通过数组,检测关键字例如"var","def"然后调用存储变量/函数的var、def函数,并将它们存储在列表或类似的数据结构中。但是,我该如何存储数据以便稍后在计算中使用呢?

此外,我不允许使用Parsec!


你可以使用 lex,对吧? - Will Ness
2
编写自己的小型解析器组合库吧!这将会很有趣,而且你将会学到很多关于单子和应用函子的知识 :) - Niklas B.
2个回答

3

看起来你有两种不同类型的输入:声明(创建新变量和函数)和表达式(计算事物)。

你应该首先定义一些数据结构,以便确定你要处理的是什么类型的东西。可以类似这样:

data Command = Define Definition | Calculate Expression | Quit
type Name = String
data Definition = DefVar Name Expression | DefFunc Name [Name] Expression
-- ^ alternatively, implement variables as zero-argument functions
-- and merge these cases
data Expression = Var Name | Add Expression Expression | -- ... other stuff
type Environment = [Definition]

首先,将其解析为Command(可能需要对其进行标记化和解析),然后决定对其采取什么操作。
表达式相对简单。假设您已经拥有了所有必需的定义(即Environment),然后只需查找任何变量或进行加法运算等操作。
定义有点棘手。一旦您决定创建新定义,就需要将其添加到环境中。如何准确地执行此操作取决于您如何迭代遍历行,但您需要将新环境从解释器传回到提取下一行并在其上运行解释器的事物。例如:
main :: IO ()
main = mainLoop emptyEnv
 where
  emptyEnv = []

mainLoop :: Environment -> IO ()
mainLoop env = do
  str <- getLine
  case parseCommnad str of
    Nothing -> do
      putStrLn "parse failed!"
      mainLoop env
    Just Quit -> do
      return ()
    Just (Define d) -> do
      mainLoop (d : env)
    Just (Calculate e) -> do
      putStrLn (calc env e)
      mainLoop env

-- the real meat:
parseCommand :: String -> Maybe Command
calc :: Environment -> Expression -> String -- or Integer or some other appropriate type
calc 需要在创建环境时查找相关信息,因此您可能还需要一个函数来查找给定 Name 对应的 Definition(或者报告找不到该定义)。
一些其他的决策也需要您做出:
- 当有人试图重新定义变量时,我该怎么办? - 如果我在函数的定义中使用了其中一个变量,那么该怎么办?我是在创建函数定义时还是在使用函数时计算它?
这些问题可能会影响上述程序的设计,但我会让您自行解决。

谢谢,我需要处理用户重新定义变量的情况。您能进一步解释一下数据和类型的使用吗?我认为当一个变量/函数被声明时,我需要存储它的名称、参数和表达式。因此,我声明了一个新类型: type VariableDef Name [Parameter] Value = (Name,[Parameter],Value) 问题是什么时候使用"type",什么时候使用"data"?而且我是否需要为每种类型(即Parameter、Value等)进行定义,因为它们是新的? - Charlie Victor
你给出的 type 语法是错误的:你不需要让你的类型同义词带有参数,只需要 type VariableDef = (Name, [Parameter], Value) 就可以了。何时使用 typedata - 在这种情况下并不太重要,但我倾向于使用 datatype 所做的就是为某些元组类型创建一个同义词。你可以传入不是变量定义的元组,它也可以通过类型检查。定义一个新的数据类型可以确保这种情况不会发生。我不明白你的最后一个问题。显然,你不能使用未定义的类型。 - Ben Millwood
哦,我的意思是举个例子: 数据定义 = DefVar名称表达式 | DefFunc名称[名称]表达式。 我们需要定义DefVar、DefFunc、Name、Expression吗? - Charlie Victor
1
数据声明的形式为 data TypeName = ConstructorName ParameterType1 ParameterType2 ... | OtherConstructor MoreTypes ...。 构造函数的参数必须是在其他地方声明的类型,但构造函数本身是由 data 声明 创建 的。 因此 DefVarDefFunc 不需要已经存在。 - Ben Millwood

1
首先,您可以从这个Haskell编程教程中学到很多东西。
您需要在另一个扩展名为.hs的文档中编写您的函数。 然后,您可以从编译器中加载该文件并使用您创建的所有函数。 例如:
plus :: Int -> Int  -- that mean the function just work with a number of type int and return Int
plus x y = x + y    -- they receive x and y and do the operation 

1
嗨,我知道Haskell的基础知识,因为这是我“Haskell入门课程”的任务。实际上,它比仅编写一个函数来计算两个数字更为复杂。用户将对任意数量的变量执行任何类型的计算。如果他想计算a+b/c*d-e怎么办?此外,用户需要能够将数字存储在变量中,而不仅仅是将直接数字提供给程序(如上面所示的示例)。 - Charlie Victor
你好,我喜欢你的文章。你有很多贡献可以做出。花点时间思考如何应用你的见解,少猜测,多解决问题。这需要真正的投入,但我希望你坚持下去。祝你幸福快乐。 - Dave Alperovich
我希望我能做,但问题是时间太紧了,我只有不到一周的时间来完成这个任务。此外,对我来说,在Haskell中编写程序就像用外星语言写作文一样。我需要学习所有新的语法和词汇:( - Charlie Victor
如果有人需要所有函数的帮助,一个好的部分是基于Lisp函数。而且Lisp有更多的信息。 - Benius

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接