在Haskell中使用'signum'作为有序数值类型的用例

enr*_*que 0 haskell sign absolute-value

signum函数是sign的通常数学定义的实现,它有条件地返回{-1,0,1}中的值.这是一个理论定义,因此,它没有考虑操作的计算成本或值的数据类型,因此乘以(-1)是改变符号的零成本理论方法.因此,它不是最有用的编程符号处理方法.

这种情况signum a == 0并不是很有用,因为您可以随时直接测试a == 0,而无需额外的计算成本signum a.至于其他两个值,我认为它们仅用于三种方式:

在所有情况下,Bool标志的价值会更好.因此,我们只需要将函数分解Num为绝对值和布尔符号,以及反函数,它根据布尔条件(代表符号)更改值的符号.此函数相当于乘以1或乘以-1数字,因此我们将其定义为类似的运算符(*).:

sgn  a      = a >= 0
(*.) a True = a
(*.) a _    = -a
abs  a      = a *. sgn a
signum1 a   = 1 *. sgn a
Run Code Online (Sandbox Code Playgroud)

我添加了一个二分变量,signum只能返回'{-1,1}'.请注意,在它之前signum 0 = 0我们将得到通常的signum函数,但第三种情况是我认为通常不常用的.

我们同样可以编写一个添加操作,因为这是一个很常见的情况增加1-1取决于事物的符号(你可以看到,这些运营商只是把True作为1False作为-1):

(+.) a b    = a + 1 *. b
(-.) a b    = a - 1 *. b
Run Code Online (Sandbox Code Playgroud)

我们甚至可以将声明括在一个名为的类中Signed,以便于使用,包括正确的签名和固定性.

这样,上面的一般例子不仅简化了代码,而且简化了执行时间和空间,因为我们避免乘法((*.)改为使用),我们避免了额外的比较,一旦得到a Bool,我们可以从一种类型获得符号数据并将其用于其他类型而无需进行类型转换,我们使用short类型Bool而不是可能长类型的​​类Num.但我们获得了更多的灵活性,同时允许对代码和数据类型进行一些优化.

那么,我的问题是,是否存在与此处公开的三个一般用例不同的情况,即这种方法不容易涵盖的情况,当前signum函数对Bool符号方法有利的情况.更准确地说,我可以完全避免使用当前signum功能而不会降低效率或代码清晰度吗?


编辑:在Reid Barton发表评论之后,我将第一段修改为更"中立"的方式.


进度更新:在当前答案和评论的帮助下,这种方法的代码在简洁和清晰方面得到了极大的改进.

Dan*_*ner 10

你假设"正面"和"负面"是唯一两个可能的迹象.但是对于例如Complex Double,signum操作返回具有相同"方向"但幅度为1的复数:

Data.Complex> signum (3 :+ 4)
0.6 :+ 0.8
Run Code Online (Sandbox Code Playgroud)

  • 是的,但这就提出了为什么这被称为"signum"的问题.可以说这个操作应该被称为`normalise`或类似的东西,并且它不应该将0映射到0(因为它没有单位模数). (3认同)