更适合阅读 Haskell 中的科学记数法
Posted
技术标签:
【中文标题】更适合阅读 Haskell 中的科学记数法【英文标题】:More accommodating read for scientific notation in Haskell 【发布时间】:2018-04-28 16:13:56 【问题描述】:Haskell 的 read
对浮点数有点过于严格了:
$ ghci
GHCi, version 8.2.2: http://www.haskell.org/ghc/ :? for help
Prelude> read "-1E34" :: Double
-1.0e34
Prelude> read "-1.E34" :: Double
*** Exception: Prelude.read: no parse
Prelude>
是否有接受第二种形式的 read 版本?这在物理科学中很常见。例如,Fortran 读取和写入此类表单。
Haskell 不支持的另一个例子是“0.1”的“.1”。这个更常见。我只是不想转换输入的 ascii 文件。 . . .
【问题讨论】:
也许写另一个函数将字符串解析为可用于读取的等效格式? 我一直认为 Haskell 应该支持这样的文字。 There are reasons not to do it in the language,但我真的没有理由在read
中不支持它。幸运的是,自己定义一个数字解析器很容易(并且比 Read
的预处理更好)。
【参考方案1】:
这是一个使用 megaparsec 的自定义解析器。
import Text.Megaparsec
import Text.Megaparsec.Char
realLiteral :: (MonadParsec e s m, Token s ~ Char) => m Double
realLiteral = mkFloat <$> sign <*> intgPart <*> fracPart <*> exponent
where mkFloat sgn itg frc expn
= fromIntegral sgn * (fromIntegral itg + frc) * 10^^expn
sign = (-1) <$ char '-'
<|> 1 <$ char '+'
<|> pure 1
intgPart = read . ('0':) <$> many digitChar
fracPart = char '.' *> (toFrc<$>many digitChar)
<|> pure 0
where toFrc "" = 0
toFrc digits = read digits / 10^length digits
exponent = oneOf "eEdD" *> ((*) <$> sign <*> (read<$>some digitChar))
<|> pure 0
[1 of 1] 编译主程序(wtmpf-file5764.hs,解释) 好的,已加载 1 个模块。 *Main> parseMaybe realLiteral "1" 仅 1.0 *Main> parseMaybe realLiteral "-3" 只是(-3.0) *Main> parseMaybe realLiteral "-9e+2" 只是 (-900.0) *Main> parseMaybe realLiteral ".3e+9" 只需 3.0e8 *Main> parseMaybe realLiteral "-1.E34" 只是(-1.0000000000000001e34) *Main> parseMaybe realLiteral "-1.673986e-40" 只是(-1.6739859999999999e-40) *Main> parseMaybe realLiteral "-3.E+16" 只是(-3.0e16)
【讨论】:
【参考方案2】:作为Somebody said,您可以创建一个(或三个)辅助函数来帮助将数字转换为适用于read
的格式。我对 Haskell 不是最好的,所以我不确定还有哪些其他解决方案,但我写了一些函数来提供帮助。我已经用read
对其进行了测试,到目前为止一切正常。
prefixZero :: String -> String
prefixZero "" = ""
prefixZero ('-' : xs) = '-' : '0' : xs
prefixZero s = '0' : s
suffixZero :: String -> String
suffixZero "" = ""
suffixZero ('.' : exp@('E' : _)) = '.' : '0' : exp
suffixZero (x : xs) = x : suffixZero xs
format :: String -> String
format = suffixZero . prefixZero
您可以调用以下代码:
read (format "-1.E34") :: Double
输出如下:
-1.0e34
【讨论】:
以上是关于更适合阅读 Haskell 中的科学记数法的主要内容,如果未能解决你的问题,请参考以下文章
SQL Server 2008 - 从 Float 将列更改为 Varchar 产生科学记数法