4

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

4

2 回答 2

14

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

这是一个简化术语的示例(来自本文):

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

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

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

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

  • 在 Scala 中,每个 case 也是一种类型,因此您可以例如定义一个采用NumorMul作为参数的方法。在 F# 中,这是不可能的,因为NumandMul只是 type 的构造函数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
    
于 2010-05-24T14:02:28.900 回答
-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)
  }
}

我们可以看到风格相当OO。

以下是我的 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 更加面向对象。

于 2010-05-24T05:22:54.387 回答