其实我有一些类似于"x + y"
这样的公式,它是一个String
类型的。
我已经使用具体数值(例如"1.2"
)替换了x/y
变量,但它们仍然是String
类型。现在我有了一个类似"1 + 2"
的表达式。
所以问题是如何计算字符串类型的表达式并得到结果。
附:我想要像read
这样的东西,可以直接转换整个字符串表达式,而不是逐个处理运算符(+/-等)情况。这可能吗?
您的问题存在很大的理解空间。我猜测您可能不习惯构建整个词法分析、语法分析、类型检查和求值管道。长答案需要您定义要评估的语言(仅带'+'的整数,也许包括所有有'+', '-', '*', '/'运算符的有理数,甚至更大的语言?),并对该语言执行上述每个步骤。
简短的答案是:要评估Haskell表达式,其中包括您可能在谈论的基本数学运算符,请使用"hint"包:
$ cabal install hint
...
$ ghci
> import Language.Haskell.Interpreter
> runInterpreter $ setImports ["Prelude"] >> eval "3 + 5"
Right "8"
太棒了!
interpret
函数将快速为您提供多态类型的结果(而不是字符串表示),根据您的需要,这可能会更好。祝编程愉快。 - Thomas M. DuBuissonhint
? - vikingsteve阅读Real World Haskell的Parsec部分可能会很有价值。您可以将其解析为表达式树,然后替换其中的值。使用Parsec时,您将使用以下类型(非常粗略,我肯定犯了一些错误,我将在人们指出错误时进行修正!)构建表达式树。
data Op = Plus | Minus
data Term = Variable String
| Value Int
data Expression = Expr Expression Op Expression
| Term
(Expr (Variable "x") Plus (Variable "y"))
,你可以应用相应的替换。为了得到结果,我猜你可以编写一个简单的函数evaluate :: Map String Int -> Expression -> Either ErrorMessage Int
,它将应用映射中的绑定,然后在可能的情况下计算结果。eval :: String -> Double
。更具Haskell风格的可能是eval :: String -> Either ErrorMessage Double
。然而,正如大多数人指出的那样:用字符串表示表达式是不好的。最好使用像Jeff展示的语法树。 - Tarrasch我一直在苦苦思索如何使用hint,但现在我放弃了。我知道hint可以做到这一点,但我不确定如何操作。
[编辑]请参考TomMD的答案,了解如何为hint设置导入。
import Language.Haskell.Interpreter (eval, runInterpreter, Interpreter, InterpreterError)
main = do let resIO = eval "3" :: Interpreter String
res <- runInterpreter resIO
print res
这样做会无聊地产生 Right "3"
作为结果。我尝试了以下变体,但遇到了令人困惑的错误:
... eval "3 + 3" ....
-- yields --
Left (WontCompile [GhcError (errMsg = "Not in scope: `+'"])
+
运算符不在范围内???什么鬼...import Language.Haskell.Interpreter (interpret, as, runInterpreter, Interpreter)
main = do let resIO = interpret "3" (as :: Int) :: Interpreter Int
res <- runInterpreter resIO
print res
-- yields --
Left (WontCompile [GhcError (errMsg = "Not in scope: type constructor or class 'Int'")])
Int
类不在作用域内???烦人...
我邀请比我更有知识的人来详细阐述提示的细节。
setImports
,请参考我的回答。 - Thomas M. DuBuisson被接受的答案展示了使用提示的最小示例,但它缺少一些东西:
let x = 1 in x + 1
。这里是一个更完整的示例:
import qualified Control.DeepSeq as DS
import Control.Exception (ArithException (..))
import qualified Control.Exception as Ex
import qualified Control.Monad as M
import qualified Data.Either as E
import qualified Language.Haskell.Interpreter as I
evalExpr :: String -> [(String, Integer)] -> IO (Maybe Integer)
evalExpr expr a = Ex.handle handler $ do
i <- I.runInterpreter $ do
I.setImports ["Prelude"]
-- let var = value works too
let stmts = map (\(var, val) -> var ++ " <- return " ++ show val) a
M.forM_ stmts $ \s -> do
I.runStmt s
I.interpret expr (I.as :: Integer)
-- without force, exception is not caught
(Ex.evaluate . DS.force) (E.either (const Nothing) Just i)
where
handler :: ArithException -> IO (Maybe Integer)
handler DivideByZero = return Nothing
handler ex = error $ show ex
eval "1+2" :: Int
这样的东西吗? - rampionText.Parsec
是Haskell平台的一部分,它可以让你做到:runP (do { x <- many1 digit; spaces; string "+"; spaces; y <- many1 digit; return $ read x + read y }) () "-" "1 + 2"
返回Right 3
。但你可能不仅仅想要加法。你想要一个计算器吗?一个完整的Haskell解释器? - rampion