Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
'的用例;符号';对于Haskell中的有序数字类型_Haskell_Sign_Absolute Value - Fatal编程技术网

'的用例;符号';对于Haskell中的有序数字类型

'的用例;符号';对于Haskell中的有序数字类型,haskell,sign,absolute-value,Haskell,Sign,Absolute Value,signum函数是普通函数的实现,它有条件地返回{-1,0,1}中的值。这是一个理论定义,因此,它不考虑操作的计算成本或值的数据类型,因此乘以(-1)是一种零成本的改变符号的理论方法。正因为如此,它不是编程中最有用的符号处理方法 案例signum a==0并非真正有用,因为您总是可以直接测试a==0,而不需要额外的计算成本signum a。至于其他两个值,我认为它们仅以3种一般方式使用: 您可以测试某个值是正值还是负值,以便有条件地启动不同的代码,如: f x y | signum x ==

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

案例
signum a==0
并非真正有用,因为您总是可以直接测试
a==0
,而不需要额外的计算成本
signum a
。至于其他两个值,我认为它们仅以3种一般方式使用:

  • 您可以测试某个值是正值还是负值,以便有条件地启动不同的代码,如:

    f x y | signum x == -1  = h x y
          | otherwise       = g x y
    
  • 或者在操作某个对象之前,将其乘以
    1
    -1
    ,如:

    f x y = g x (y * b) where 
          b = signum x
    
    f x y = g x (y + b) where 
          b = signum x
    
  • 或者在操作之前,将
    1
    -1
    添加到某个对象,如:

    f x y = g x (y * b) where 
          b = signum x
    
    f x y = g x (y + b) where 
          b = signum x
    
在所有情况下,最好使用符号的
Bool
值。因此,我们只需要函数将
Num
分解为绝对值和布尔符号,以及逆函数,它根据布尔条件(表示符号)改变值的符号。此函数相当于将
1
-1
乘以一个数字,因此我们将其定义为类似于
(*)
的运算符:

sgn  a      = a >= 0
(*.) a True = a
(*.) a _    = -a
abs  a      = a *. sgn a
signum1 a   = 1 *. sgn a
我添加了一个二分法变体
signum
,它只能返回'{-1,1}'。请注意,在它前面加上
signum 0=0
我们将得到通常的
signum
函数,但我认为第三种情况通常没有用处

我们可以对加法运算符进行类似的编码,因为根据某物的符号添加
1
-1
是非常常见的情况(您可以看到,这些运算符只将
True
视为
1
,将
False
视为
-1
):

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

这样,上述通用示例不仅在代码方面,而且在执行时间和空间方面都会简化,因为我们避免了乘法(使用
(*)
),我们避免了额外的比较一旦我们有了
Bool
,我们就可以从一种类型的数据中获取符号并将其用于另一种类型,而无需进行类型转换,我们使用短类型
Bool
,而不是潜在的长类型的类
Num
。但是我们获得了更多的灵活性,同时允许对代码和数据类型进行一些优化

那么,我的问题是,是否有不同于这里公开的三个通用用例的情况,即这种方法不容易涵盖的情况,当前的
signum
函数对Bool-sign方法有利的情况。更准确地说,我是否可以完全避免使用当前的
signum
函数,而不损失效率或代码清晰度


编辑:在里德·巴顿发表评论之后,我将第一段修改为更“中立”的方式


进度更新:在当前答案和评论的帮助下,该方法的代码得到了极大的改进,以简化和清晰

您假设“积极”和“消极”是仅有的两种可能的迹象。但对于例如
复数双精度
符号
操作返回一个具有相同“方向”但大小为1的复数:

Data.Complex> signum (3 :+ 4)
0.6 :+ 0.8
你假设“积极”和“消极”是仅有的两个可能的迹象。但对于例如
复数双精度
符号
操作返回一个具有相同“方向”但大小为1的复数:

Data.Complex> signum (3 :+ 4)
0.6 :+ 0.8

我使用了这样一个函数,通过向(正交和对角)相邻单元格的一系列移动,将光标导航到正方形网格中的目标偏移

move :: (Int, Int) -> [(Int, Int)]
move (0, 0) = []
move (x, y) = (dx, dy) : move (x - dx, y - dy)
  where dx = signum x
        dy = signum y

我使用了这样一个函数,通过向(正交和对角)相邻单元格的一系列移动,将光标导航到正方形网格中的目标偏移

move :: (Int, Int) -> [(Int, Int)]
move (0, 0) = []
move (x, y) = (dx, dy) : move (x - dx, y - dy)
  where dx = signum x
        dy = signum y

为了解决时间复杂性问题:

分支不是自由的,如果必须(在概念上)将值乘以多个不同点上相同值的符号的结果,则在…中使用
让s=signum x或在
where
-子句中使用该绑定可能更有效。你不再需要每次都通过一个分支。还要记住这一点

例如,假设您有如下代码:

f x y z = (s * y) / (1 + (s * z))
  where
    s = signum x

效率分析通常不像您预期的那样清晰,并且高度依赖于特定计划的特定方面,正如我在上面链接的问题中所看到的,因此经常引用的建议是“优化前概要”。在我链接到的问题中,执行更多指令的代码版本实际上比执行更少指令的代码版本运行得更快(我可以在我的机器上验证这些结果,即使我在评测中包含了额外的排序指令)

要解决时间复杂性问题:

分支不是自由的,如果必须(在概念上)将值乘以多个不同点上相同值的符号的结果,则在…
中使用
让s=signum x或在
where
-子句中使用该绑定可能更有效。你不再需要每次都通过一个分支。还要记住这一点

例如,假设您有如下代码:

f x y z = (s * y) / (1 + (s * z))
  where
    s = signum x
效率分析通常不像您预期的那样清晰,并且高度依赖于特定计划的特定方面,正如我在上面链接的问题中所看到的,因此经常引用的建议是“优化前概要”。在我链接到的问题中