更适合阅读 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 产生科学记数法

EXCEL VBA:用户表单文本框中的科学记数法

如何在linux或perl的特定列中将科学记数法更改为数字

如何在由科学记数法组成的列表中将字符串更改为 int

NSNumberFormatter、NSDecimalNumber 和科学记数法

matplotlib 中的科学记数法颜色条