% 在 Haskell 中有什么作用?

What does % do in Haskell?

我习惯用 % 在其他语言中表示 "modulo"。在Haskell中,我们必须使用mod x yx `mod` y。那么,这个符号在Haskell中有什么用呢?

Searching for (%) on Stackage Hoogle,似乎 Data.Ratio% 运算符定义为从分子和分母构造 Ratio 值。 GHCi 示例:

Prelude> :m + Data.Ratio
Prelude Data.Ratio> let x = 1 % 2
Prelude Data.Ratio> x
1 % 2
Prelude Data.Ratio> :t x
x :: Integral a => Ratio a

快速查看 Hoogle,您可以看到 % 是一个定义为

的中缀函数
(%) :: Integral a => a -> a -> Ratio a

您可以猜到它是 Data.Ratio 库的一部分,该库主要处理比率(即:分数)。它的代码是

x % y = reduce (x * signum y) (abs y)

因此给定两个积分 (x,y) ,它 returns 一个不可约分数 x/y

在Haskell中,我们可以像普通函数一样定义各种符号(包括%)的二元运算符,所以你可以将%定义为你想要的任意运算符(在你定义它的模块)。

作为最典型的例子,%Data.Ratio module作为Ratio类型的构造函数提供。

在 GHCi 上尝试以下代码以确保 %Data.Ratio 提供:

ghci> 3 % 9

<interactive>:1:3: error:
    Variable not in scope: (%) :: Integer -> Integer -> t
ghci> import Data.Ratio
ghci> 3 % 9
1 % 3

请记住,您可以在这些搜索引擎中搜索此类运算符和函数:

其实我查过 % 是如何被 Hoogle 定义的。

% 是定义为

的中缀函数
(%) :: Integral a => a -> a -> Ratio a

并且从上面的类型定义中,您可以看到它是 Data.Ratio 库的一部分,该库主要处理比率(即:分数)。它的代码是

x % y = reduce (x * signum y) (abs y)

因此给定两个积分 (x,y) ,它 returns 一个不可约分数 x/y

Data.Ratio 使用 % 作为构造函数,但除非该类型是在 Integral 类型 class 之前定义的,否则它无法解释为什么 % 可用Data.Ratio 使用。 (当然,合格的导入允许您在多个模块中使用相同的运算符名称,所以无论哪种方式,%Data.Ratio 使用都不是真正的理由。)

但是请注意,Integral 定义了 both mod and rem functions。我怀疑 % 被故意排除在 Integral 之外,都是为了避免 1) 选择它是否应该是 modrem 的别名,以及as 2) 让人们记住做出的选择。

另外,languages use different definitions for %,所以 (%) = mod(%) = rem 都有可能混淆 某人

在“老派”戴维斯 Introduction to Functional Programming Systems Using Haskell 中找到此内容。 (他经常拿Haskell比作Pascal。)这是对栈运算的模拟

type Stack = [Float]

push :: Float -> Stack -> Stack
push x stack = x : stack

addStack :: Stack -> Stack
addStack (x:y:stack) = (y + x) : stack

subtStack :: Stack -> Stack
subtStack (x:y:stack) = (y - x) : stack

multStack :: Stack -> Stack
multStack  (x:y:stack) = (y * x) : stack

divStack :: Stack -> Stack
divStack  (x:y:stack) = (y / x) : stack

emptyStack :: Stack
emptyStack = []

popStack :: Stack -> (Float, Stack)
popStack (top:rest) = (top,rest)

然后

let f % g     = g . f
    actionsOn = push 12.2 %
                push 7.1 %
                push 6.7 %
                divStack %
                push 4.3 %
                subtStack %
                multStack %
                push 2.2 %
                addStack
in popStack (actionsOn emptyStack)

(-37.331642,[])

这只是看起来很疯狂的嵌套函数的简洁版本

popStack (addStack (push 2.2 (multStack (subtStack (push 4.3 (divStack (push 6.7 (push 7.1 (push 12.2 emptyStack)))))))))

它本身避免为每个堆栈操作创建和传递新堆栈。总而言之,YAMAMOTO Yuji 一开始所说的适用于此,而不是任何 Ratio 东西 AFAIK。