如何在 Haskell 中将字符串解析为函数? [英] How can I parse a string to a function in Haskell?
问题描述
我想要一个看起来像这样的函数
I want a function that looks something like this
readFunc :: String -> (Float -> Float)
它的操作是这样的
>(readFunc "sin") (pi/2)
>1.0
>(readFunc "(+2)") 3.0
>5.0
>(readFunc "(\x -> if x > 5.0 then 5.0 else x)") 2.0
>2.0
>(readFunc "(\x -> if x > 5.0 then 5.0 else x)") 7.0
>5.0
难以置信的天真方法(注意这必须用{-# LANGUAGE FlexibleContexts #-}
编译)
The incredibly naive approach (note this must be compiled with {-# LANGUAGE FlexibleContexts #-}
)
readFunc :: (Read (Float -> Float)) => String -> (Float -> Float)
readFunc s = read s
给予
No instance for (Read (Float -> Float)) ...
这是有道理的,因为不存在这样的实例.我知道我可以通过编写从 String
到 Float -> 的映射来逐个字符地解析输入字符串.Float
但我希望至少能够解析 prelude 中最常见的函数,即使这样也比我想要的工作量要多得多.有没有简单的方法可以做到这一点?
Which makes sense since no such instance exists. I understand that I can parse the input string character by character by writing a map from String
to Float -> Float
but I want to be able to parse at least the most common functions from prelude, and even that would be way more work than I want to commit to. Is there an easy way of doing this?
import Language.Haskell.Interpreter hiding (typeOf)
import Data.Typeable (typeOf)
data Domain = Dom Float Float Float Float Domain
| SDom Float Float Float Float
deriving (Show, Read)
--gets all the points that will appear in the domain
points (SDom a b c d) m = [(x, y)|x <- [a, a+m .. b], y <- [c, c+m .. d]]
points (Dom a b c d next) m = points next m ++ [(x, y)|x <- [a, a+m .. b], y <- [c, c+m .. d]]
readFunc = do
putStrLn "Enter a domain (as Dom x-min x-max y-min y-max subdomain, or, SDom x-min x-max y-min y-max)"
domain' <- getLine
let domain = (read domain') :: Domain
--
putStrLn "Enter a mesh size"
meshSize' <- getLine
let meshSize = (read meshSize') :: Float
--
putStrLn "Enter an initial value function (as f(x,y))"
func' <- getLine
values' <- runInterpreter $ setImports["Prelude"] >>
eval ("map (\\(x,y) -> " ++ func' ++ ")" ++ show (points domain meshSize))
let values = (\(Right v) -> (read v)::([Float])) values'
--the haskell expression being evaluated
putStrLn $ ("map (\\(x,y) -> " ++ func' ++ ")" ++ show (points domain meshSize))
--prints the actual values
putStrLn $ show values
--the type is indeed [float]
putStrLn $ show $ typeOf values
推荐答案
您可以使用提示 包,或插件.我将向您展示前者(部分原因是我的 Windows 安装显然有点损坏,因为 cabal 不同意我安装 C 的信念,因此 cabal 安装插件失败).
You can use the hint package, or plugins. I'll show you the former (partly because my Windows installation is clearly a little broken in that cabal doesn't share my belief that I have C installed, so cabal install plugins fails).
import Language.Haskell.Interpreter
getF :: String -> IO (Either InterpreterError (Float -> Float))
getF xs = runInterpreter $ do
setImports ["Prelude"]
interpret xs (as :: Float -> Float)
您可能希望将其他模块添加到导入列表中.这测试为
You may want to add additional modules to the imports list. This tests out as
ghci> getF "sin" >>= \(Right f) -> print $ f (3.1415927/2)
1.0
ghci> getF "(\\x -> if x > 5.0 then 5.0 else x)" >>= \(Right f) -> print $ f 7
5.0
(注意转义字符 \
的转义.)
(Notice the escaping of the escape character \
.)
您可能已经注意到,结果被封装在Either 数据类型中.Right f
是正确的输出,而 Left err
给出了一个 InterpreterError
消息,这很有帮助:
As you may have noticed, the result is wrapped in the Either data type. Right f
is correct output, whereas Left err
gives an InterpreterError
message, which is quite helpful:
ghci> getF "sinhh" >>= \(Left err) -> print err
WontCompile [GhcError {errMsg = "Not in scope: `sinhh'\nPerhaps you meant `sinh' (imported from Prelude)"}]
示例玩具程序
当然,您可以在代码中使用 either
来解决这个问题.让我们做一个假的例子respond
.你真正的将包含你程序的所有数学.
Example toy program
Of course, you can use either
with your code to deal with this. Let's make a fake example respond
. Your real one will contain all the maths of your program.
respond :: (Float -> Float) -> IO ()
respond f = do
-- insert cunning numerical method instead of
let result = f 5
print result
一个简单的、一次尝试的、无用的程序版本可能是
A simple, one-try, unhelpful version of your program could then be
main =
putStrLn "Enter your function please:"
>> getLine
>>= getF
>>= either print respond
示例会话
ghci> main
Enter your function please:
\x -> x^2 + 4
29.0
ghci> main
Enter your function please:
ln
WontCompile [GhcError {errMsg = "Not in scope: `ln'"}]
它会为您进行类型检查:
It does type checking for you:
ghci> main
Enter your function please:
(:"yo")
WontCompile [GhcError {errMsg = "Couldn't match expected type `GHC.Types.Float'\n with actual type `GHC.Types.Char'"}]
这篇关于如何在 Haskell 中将字符串解析为函数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!