F#和Scala中的ADT

时间:2010-05-24 04:52:41

标签: scala f# comparison algebraic-data-types

F#和Scala中ADT之间的主要区别是什么?有没有F#的ADT可以做但Scala的ADT不能(反之亦然)?

2 个答案:

答案 0 :(得分:14)

从概念上讲,我认为两种语言都提供相同的功能 - 在F#中,您可以使用区分联合声明ADT,而在Scala中,您可以使用案例类。 Scala中使用类的声明可能会比F#版本稍长一些(正如Yin Zhu所指出的那样),但是你可以在两种语言中使用类似优点的模式匹配。

以下是简化术语的示例(from this article):

def simplify(term: Term) = term match {
  case Mul(Num(0), x) => Num(0)
  case Mul(Num(1), x) => x
  case _ => term
}

使用match的F#中的相同代码看起来非常相似:

let simplify term = 
  match term with 
  | Mul(Num(0), x) -> Num(0)
  | Mul(Num(1), x) -> x
  | _ -> term

差异我认为在更高级(相关)功能方面存在一些差异。

  • 在Scala中,每个案例也是一个类型,因此您可以定义一个以NumMul作为参数的方法。在F#中,这是不可能的,因为NumMul只是Term类型的构造函数。我想这可能有时很有用,但大多数时候,无论如何,你都会使用Term类型的值。

  • 与前一点相关 - 在Scala中,您还可以为单个案例定义方法。例如,您可以在Num类中定义方法。在F#中,所有成员都必须是Term类型的成员。

  • 在F#中,您可以使用活动模式来隐藏类型的内部表示(例如,从模块导出时)。这对于库设计非常有用。例如,您可以定义活动模式:

    val (|Mul|_|) // return Some(..) if Term represents multiplication
    val (|Num|_|) // return Some(..) if Term represents number
    

    内部表示可以随着时间的推移而不影响库接口,因此您可以实现如下界面:

    type Term = Binary of string * Term * Term | Num of int
    let (|Num|_|) = function Num n -> Some n | _ -> None 
    let (|Mul|_|) = function Binary("*", a, b) -> Some(a, b) | _ -> None
    

答案 1 :(得分:-2)

首先,没有语言可以,其他不能。

风格有所不同。

我读了一点Scala,但不能写。我认为二元搜索树是比较ADT风格的一个很好的例子。

这是来自http://aperiodic.net/phil/scala/s-99/的代码:

package binarytree {

  sealed abstract class Tree[+T]

  case class Node[+T](value: T, left: Tree[T], right: Tree[T]) extends Tree[T] {
    override def toString = "T(" + value.toString + " " + left.toString + " " + right.toString + ")"
  }

  case object End extends Tree[Nothing] {
    override def toString = "."
  }

  object Node {
    def apply[T](value: T): Node[T] = Node(value, End, End)
  }
}

我们可以看到风格很大。

以下是我的Treap代码(平衡二叉搜索树)的主要部分:

module Treap = 

    type node<'T> = {left: treap<'T>; right: treap<'T>; value: 'T; priority:int}
    and treap<'T>  = Empty | Node of node<'T>

    let rec lookup (k:'a) (t:'a treap) =
        match t with
            | Empty -> None
            | Node(n) -> 
                if k = n.value then Some(k)
                elif k < n.value then lookup k (n.left)
                else lookup k (n.right)

    let add (k:'a) (p:int) (tree:'a treap) = 
        let rotate xv xp yv yp a b c =
            if xp < yp then
                {value=xv; priority=xp; left=a; right=Node({value=yv;priority=yp;left=b;right=c})}
            else
                {value=yv; priority=yp; right=c; left=Node({value=xv; priority=xp; left=a; right=b})}

        let rec addNode (k:'a) (p:int) (tree:'a treap) = 
            match tree with
            | Empty -> {value=k; priority=p; left=Empty; right=Empty}, false
            | Node(n) ->
                if k=n.value then
                    n, true
                elif k<n.value then
                    let {value=xv; priority=xp; left=a; right=b}, dup = addNode k p (n.left) 
                    (rotate xv xp (n.value) (n.priority) a b (n.right)), dup
                else
                    let {value=yv; priority=yp; left=b; right=c}, dup = addNode k p (n.right)
                    (rotate (n.value) (n.priority) yv yp (n.left) b c), dup

        let n, dup = addNode k p tree
        Node(n)

我认为Scala也可以用这种风格写作。但是,Scala更为OO。