Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/8.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 循环一个值(枚举a,有界a)=>;A._Haskell_Enums - Fatal编程技术网

Haskell 循环一个值(枚举a,有界a)=>;A.

Haskell 循环一个值(枚举a,有界a)=>;A.,haskell,enums,Haskell,Enums,我正在寻找这对函数 previous :: (Enum a, Bounded a) => a -> a next :: (Enum a, Bounded a) => a -> a toEnum :: Enum a => Int -> a fromEnum :: Enum a => a -> Int 如果结果值受边界约束,则previous为pred::Enum a=>a->a,否则循环到另一侧(与next和succ对称) 范例

我正在寻找这对函数

previous :: (Enum a, Bounded a) => a -> a
next     :: (Enum a, Bounded a) => a -> a
  toEnum :: Enum a => Int -> a
  fromEnum :: Enum a => a -> Int
如果结果值受边界约束,则
previous
pred::Enum a=>a->a
,否则循环到另一侧(与
next
succ
对称)

范例

data X = A | B | C deriving (Bounded, Enum)
next A = B
next B = C
next C = A
如果我添加
eqa=>a
约束,这将很容易,如本文中所讨论的。 但这个约束似乎没有必要,因为
有界a=>a
类型在我看来应该能够判断它是否在有界范围内。
succ
pred
函数本身对此有某种控制,但我不希望在我的纯代码中使用异常:

Prelude> succ (maxBound :: Int)
*** Exception: Prelude.Enum.succ{Int}: tried to take `succ' of maxBound

succ
/
pred
如何在不要求
Eq a=>a
(甚至
Bounded a=>a
)的情况下对边界进行测试?我可以在自己的函数中复制这种行为吗?或者,是否有其他方法可以编写具有此行为的函数,而不需要
Eq
约束?

对于某些typeclass实例,它只是像您在示例中所做的那样定义函数:

instance Enum Ordering where
  succ LT = EQ
  succ EQ = GT
  succ GT = error "Prelude.Enum.Ordering.succ: bad argument"

  pred GT = EQ
  pred EQ = LT
  pred LT = error "Prelude.Enum.Ordering.pred: bad argument"

否则,正如您所说,您很可能需要考虑
Eq
,因为基本上必须有一些比较的概念(为了将边界与当前值进行比较),如果类型的完整空间无法轻松枚举。

Enum类定义了两个函数

previous :: (Enum a, Bounded a) => a -> a
next     :: (Enum a, Bounded a) => a -> a
  toEnum :: Enum a => Int -> a
  fromEnum :: Enum a => a -> Int
如果
Enum
的范围符合
Int
的大小,则可以使用这些函数将一个
Enum
与另一个进行比较。如果您愿意接受这个小小的限制,您可以按如下方式编写函数

{-# LANGUAGE ScopedTypeVariables #-}

previous :: forall a . (Enum a, Bounded a) => a -> a
previous x | from x > from minBound = pred x 
           | otherwise              = maxBound where from :: a -> Int; from = fromEnum 

next :: forall a . (Enum a, Bounded a) => a -> a
next x | from x < from maxBound = succ x 
       | otherwise              = minBound where from :: a -> Int; from = fromEnum  

有什么问题吗?如果您也可以添加
Eq
类,则很容易:

module CycleEnum where

data XEnum = A1 | A2 | A3 deriving (Bounded, Enum, Eq, Show)

cyclePrev :: (Eq a, Enum a, Bounded a) => a -> a
cyclePrev x = if x == minBound then maxBound else pred x

cycleNext :: (Eq a, Enum a, Bounded a) => a -> a
cycleNext x = if x == maxBound then minBound else succ x
如果没有
Eq
,它会稍微复杂一些

module CycleEnum where

data XEnum = A1 | A2 | A3 deriving (Bounded, Enum, Show)

isSingle :: [a] -> Bool
isSingle [x] = True
isSingle _ = False

isLastElement :: Enum a => a -> Bool
isLastElement x = isSingle $ enumFrom x

isFirstElement :: (Enum a, Bounded a) => a -> Bool
isFirstElement x = isSingle $ enumFromTo minBound x

cyclePrev :: (Enum a, Bounded a) => a -> a
cyclePrev x = if isFirstElement x then maxBound else pred x

cycleNext :: (Enum a, Bounded a) => a -> a
cycleNext x = if isLastElement x then minBound else succ x

请注意,我使用模式匹配编写了
isSingle
,而不是调用
length
;因为如果您只想知道列表是否只包含一个元素,那么计算列表的长度可能会非常低效。

suc
是由一些可以确定它是否有效的实现提供的。如果没有
(有界a,等式a)=>a
,如何计算列表的长度?这是一个“原语可以打破规则”的案例吗?我认为这只是因为
succ
不需要任何边界或相等性测试;它只需要有一个函数
a->a
。确切地说,它可以(毫无用处地)与
id
函数相同。@Mephy:在给定类型的
Enum
实例中,
succ
是专门为该类型定义的,而不是一般的,并且可以利用该类型的任何信息。例如,这里为
排序定义了
succ
。任何关于
排序的功能都是公平的游戏<代码>成功
通常可以使用,但每种类型只实现一次。@LouisWasserman我的疏忽,谢谢你的解释。这看起来很棒!这些限制是否总是被类型系统捕获?在GHCi上试用时,
previous 10
会出现编译时间错误,而
previous(10::Int)
工作正常。不,它们不是。如果
Enum
的实例包含的元素多于
Int
的大小,则
from/toEnum
只需中断
succ
pred
可能仍然有效。
previous 10
是一个有效的表达式,其类型为
Num a,Enum a,Bounded a=>a
,但是,GHCI无法打印它,因为它不知道为
a
选择哪种类型。通常它选择
整数
,但这不是
有界的
。如果您在GHCI会话中键入
default(Int)
,那么它将尝试默认为
Int
——因此
之前的10个
将默认为
Int
,并且在没有类型签名的情况下工作。此解决方案显然与用户2407038的答案具有相同的限制(
cycleNext 10
给出编译时间错误,
cycleNext(10::Int)
工作正常),但不需要
-XScopedTypeVariables
。感谢您的贡献。