Chinaunix

标题: haskell如何实现首尾相连的“环” [打印本页]

作者: pass12163com    时间: 2009-04-27 01:30
标题: haskell如何实现首尾相连的“环”
haskell如何实现“环”——首尾相连

data Week = Monday | Tuesday | Wednesday | Thursday | Friday | Saturday | Sunday
        deriving (Show,Enum,Eq)

想要实现:next  Monday = Tuesday,next Tuesday = Wednesday...next Sunday应该是Monday,如此循环。

succ Sunday和pred Monday都是非法的
现在只能如下表达

next :: Week->Week
next x |x==Sunday = Monday
         |otherwise = succ x

能否有更一般更抽象的实现呢?

[ 本帖最后由 pass12163com 于 2009-4-27 01:33 编辑 ]
作者: izhier    时间: 2009-04-27 09:49
这一个可以吗?
用cycle实现首尾相连

  1. week = [Mon, Tue, Wed, Thu, Fri, Sat, Sun]

  2. last.take n.cycle $ week
复制代码

作者: izhier    时间: 2009-04-27 10:03
这个也可以

next :: Week -> Week
next x = toEnum $ mod (fromEnum x + 1) 7
作者: bengshi    时间: 2009-04-27 10:45
原帖由 izhier 于 2009-4-27 10:03 发表
这个也可以

next :: Week -> Week
next x = toEnum $ mod (fromEnum x + 1) 7



这个很棒。只需确定循环周期。

上例中的n是个问题。
作者: izhier    时间: 2009-04-27 11:09
原帖由 bengshi 于 2009-4-27 10:45 发表
上例中的n是个问题。


效率太低了
作者: pass12163com    时间: 2009-04-27 16:28
谢谢各位的回复。
用mod的确够一般化,只是看起来像用的数组下标概念,少了FP的意味。

[ 本帖最后由 pass12163com 于 2009-4-27 16:33 编辑 ]
作者: MMMIX    时间: 2009-04-27 17:43
原帖由 pass12163com 于 2009-4-27 01:30 发表
data Week = Monday | Tuesday | Wednesday | Thursday | Friday | Saturday | Sunday
        deriving (Show,Enum,Eq)

想要实现:next  Monday = Tuesday,next Tuesday = Wednesday...next Sunday应该是Monday,如此循环。

succ Sunday和pred Monday都是非法的

你不应该 deriving Enum,而应该自己 instance Enum。
作者: izhier    时间: 2009-04-28 09:23
原帖由 MMMIX 于 2009-4-27 17:43 发表
应该自己 instance Enum。

难道是:

  1. data Week = Mon | Tue | Wed | Thu | Fri | Sat | Sun
  2.         deriving (Show, Eq)

  3. instance Enum Week where
  4.   fromEnum day = case day of
  5.                                Mon -> 0
  6.                                Tue -> 1
  7.                                Wed -> 2
  8.                                Thu -> 3
  9.                                Fri -> 4
  10.                                Sat -> 5
  11.                                Sun -> 6
  12.                                 _   -> error "invalid value constructor"
  13.    
  14.   toEnum n = case n of
  15.                          -1 -> Sun
  16.                          0  -> Mon
  17.                          1  -> Tue
  18.                          2  -> Wed
  19.                          3  -> Thu
  20.                          4  -> Fri
  21.                          5  -> Sat
  22.                          6  -> Sun
  23.                          7  -> Mon
  24.                          _  -> error "invalid number"
复制代码

run

  1. *Main> succ Sun
  2. Mon
  3. *Main> pred Mon
  4. Sun
复制代码

[ 本帖最后由 izhier 于 2009-4-28 09:36 编辑 ]
作者: MMMIX    时间: 2009-04-28 13:05
原帖由 izhier 于 2009-4-28 09:23 发表

难道是:

data Week = Mon | Tue | Wed | Thu | Fri | Sat | Sun
        deriving (Show, Eq)


实现 toEnum 的时候对其参数先取模。
作者: izhier    时间: 2009-04-28 13:15
原帖由 MMMIX 于 2009-4-28 13:05 发表
实现 toEnum 的时候对其参数先取模。


应该是
toEnum n = case n `mod` 7 of
这样写
作者: pass12163com    时间: 2009-04-28 14:36
查了些资料也没明白instance Enum和deriving Enun的差别.......能否详解?

感觉这样绕了个弯,还不如直接枚举next Mon = Tue.....next Sun =  Mon
previous Mon = Sun....previous Sun

请赐教。
作者: izhier    时间: 2009-04-28 14:50
instance Enum Week
是把Week类型声明为Enum类,也就是实现Enum类中声明函数的重载
只需定义最小组合的函数集,其他的系统会自动生成(因为其他函数是使用这些最小组合的函数来定义的)

deriving Enun
系统自动生成 Enum instance,重载函数
但这里重载的函数是系统默认的,并没有自定义的灵活(定义自己想要的功能)

对了
deriving 有一个限制:
In the case of constructors with arguments, the types of these arguments
must also be instances of any derived classes.
就是 instance 的 构造函数的参数也必须属于 derived classes

不知以上说得对不对,只是自己的理解

[ 本帖最后由 izhier 于 2009-4-28 15:03 编辑 ]
作者: izhier    时间: 2009-04-28 14:55
想问一下: deriving (Class) 是不是语法糖 ?
作者: pass12163com    时间: 2009-04-28 15:24
谢谢详解,清晰了。
只需定义最小组合的函数集,其他的系统会自动生成(因为其他函数是使用这些最小组合的函数来定义的)

其实在本例中,由于只涉及到succ  pred或者next  previous,所以重新定义哪怕是“最小的函数集合”,也没有多少甜头。不知我这样理解对不对......

deriving (Class)是不是语法糖,这个我也没有见到过确切的文字。但我认为不是。
作者: izhier    时间: 2009-04-28 15:40
在 Prelude 模块中的 Enum 的 succ  pred 函数定义是这样的:

  1. succ                  = toEnum . (1+)       . fromEnum
  2. pred                  = toEnum . subtract 1 . fromEnum
复制代码

我们可以看到 succ 和 pred 两个函数是怎样定义的(调用 toEnum 和 fromEnum 函数)
所以我们只定义 toEnum 和 fromEnum 这两个函数,其他的都依赖于这两个函数定义
这样,我们就减少了代码量(无须定义其他函数)

如果我们想要的不是这样的定义,而是另一个 succ,那么我们完全可以自己利用 instance 来覆盖这个定义

当然,也可以覆盖 toEnum 和 fromEnum 这两个“最小的函数集合”

前面8楼的定义就是只覆盖了toEnum 和 fromEnum 这两个“最小的函数集合”。

通过改变 toEnum 和 fromEnum 这两个函数,进而改变 succ 和 pred 等 Enum 类的其他函数

[ 本帖最后由 izhier 于 2009-4-28 15:48 编辑 ]
作者: izhier    时间: 2009-04-28 15:43
我们 instance 后(8楼), succ 函数的功能既是你要的 next 函数

为何如此说:
原帖由 pass12163com 于 2009-4-28 15:24 发表
也没有多少甜头

作者: MMMIX    时间: 2009-04-28 15:55
原帖由 pass12163com 于 2009-4-28 15:24 发表
谢谢详解,清晰了。

其实在本例中,由于只涉及到succ  pred或者next  previous,所以重新定义哪怕是“最小的函数集合”,也没有多少甜头。

你想要啥甜头?

BTW,其实你可以重新定义个和 Enum 类似的 class, 例如叫做 EEnum, 然后 deriving 这个 EEnum。
作者: pass12163com    时间: 2009-04-28 15:59
呵呵,只是单纯从构建next的代码规模来说的,并无它意。
不过我想在instance的基础上重新考虑一些问题,那时可能会有很多甜头吧。
作者: izhier    时间: 2009-04-28 16:03
感觉 instance 实现更优雅一些,更具 FP 特色
作者: MMMIX    时间: 2009-04-28 16:36
原帖由 izhier 于 2009-4-28 14:55 发表
想问一下: deriving (Class) 是不是语法糖 ?

不是。如果是的话,deriving 也就不会有那么多限制了。

[ 本帖最后由 MMMIX 于 2009-4-28 16:38 编辑 ]
作者: MMMIX    时间: 2009-04-28 16:38
原帖由 izhier 于 2009-4-28 16:03 发表
感觉 instance 实现更优雅一些,更具 FP 特色

这个实际和 FP 一点关系都没有,是 Haskell 的 type class 在起作用。
作者: MMMIX    时间: 2009-04-28 16:47
原帖由 pass12163com 于 2009-4-28 15:59 发表
呵呵,只是单纯从构建next的代码规模来说的,并无它意。

有些时候,最好的方法并不一定是最省力(也即代码量最小)的方法。
作者: izhier    时间: 2009-04-28 17:57
原帖由 MMMIX 于 2009-4-28 16:38 发表
这个实际和 FP 一点关系都没有,是 Haskell 的 type class 在起作用。

嗯,应该是 haskellic
作者: izhier    时间: 2009-04-29 09:19
原帖由 izhier 于 2009-4-28 14:55 发表
想问一下: deriving (Class) 是不是语法糖 ?


作者: pass12163com    时间: 2009-04-29 22:04
这个算是haskell风格的吧:一个list头部的next就是其尾部的head,剩下的交给递归解决。当然还要定义好边界。

next :: (Eq a)=>a->[a]->a
next x xs
        |(x `elem` xs) == False = error "error"
        |x==last xs = head xs
        |x==head xs = head $ tail xs
        |otherwise = next x (tail xs)
作者: MMMIX    时间: 2009-04-30 08:09
原帖由 pass12163com 于 2009-4-29 22:04 发表
这个算是haskell风格的吧

最好的办法就是直接 instance Enum
作者: pass12163com    时间: 2009-04-30 14:00
最好的办法就是直接 instance Enum


多谢指点。

不过在数据元素很多的情况下,instance Enum重新定义函数集非常单调乏味而繁重,如果再考虑函数的多态性,那会更糟糕。您说呢?
作者: MMMIX    时间: 2009-04-30 14:32
原帖由 pass12163com 于 2009-4-30 14:00 发表


多谢指点。

不过在数据元素很多的情况下,instance Enum重新定义函数集非常单调乏味而繁重,如果再考虑函数的多态性,那会更糟糕。您说呢?

我说的最好的方法是针对顶楼的情况,不是说在所有情况下它都是最好的。




欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2