今日份的闲的无聊:一行代码实现简单的四则运算器

2018/09/03

今天在网上看到有人用 Haskell 写了一个一行代码实现四则运算的帖子,感觉十分厉害就想着自己也实现一个,但是无奈我看不懂 Haskell 代码,所以就找了一个 F# 版本的例子来模仿:

// 这里是被我展开后的版本
let eval =
    let s (c: char) (s: string) = s.Split c
    in let t (s: string) = s.Trim ()
    in let o c r f = s c >> Seq.map (t >> f) >> Seq.reduce r
    in int |> o '/' (/) |> o '*' (*) |> o '-' (-) |> o '+' (+)


可以看到,这个 eval 函数的功能并不是太复杂,他首先定义了一个函数 s,这个函数的作用是将字符串安装指定的字符分割成数组。接着又定义了一个 t 函数,作用是去除字符串首尾的空格。接着,他又定义了一个 o 函数,这个函数的首先接受一个字符 c,然后接受一个四则运算符(+、-、*、/),最后接受的是一个函数 f,这个函数的作用是把字符串转换成数字。

o 函数实际的作用是先把一个字符串按照运算符符号字符进行分割(s c)成数组,然后再把数组中的字串首尾的空格去掉同时调用 f 求出子串的值,最后,再把最终的值数组按照与四则运算符号 c 对应的函数进行 reduce

那么其中的 f 到底是怎么样的一个函数呢?为了弄明白这点,我们需要重新审视一下四则运算(不含括号)的步骤——先算加减,再算乘除。不过这里还要再加上一步——先读数字,再算加减,最后乘除。

在函数 o 的定义中我们知道,f 是要比运算符 r 先进行运算的,结合我们在上面总结出来的四则运算规律,我们可以得出:

读数字 >> 乘除 >>  加减

这也就是说,当运算符是乘除的时候,f 是一个把字符串转换成数字的函数 int,当运算符是加减的时候,f 则是先读数,再乘除。

在弄明白了 eval 函数的原理之后,我们就可以试着自己模仿一个了,我在这里选择的语言是 Elm:

eval = (\char reducer fn -> String.split char >> List.map (String.trim >> fn) >> List.foldr reducer (if char == "+" || char == "-" then 0 else 1)) |> \op -> (String.toFloat >> Maybe.withDefault 0) |> op "*" (*) |> op "/" (/) |> op "-" (-) |> op "+" (+)

由于 Elm 中 let ... in ... 语法似乎不能写在同一行,所以我们只好把函数 o 写成一个 lambda 然后再把这个函数作为参数传入 eval 中。

内容导航