【问题标题】:Haskell equivalent of data constructors in Python?Haskell 相当于 Python 中的数据构造函数?
【发布时间】:2013-09-03 08:24:59
【问题描述】:

在 Haskell 中,我可以如下定义二叉树:

data Bint a = Leaf a | Branch a (Bint a) (Bint a) 

然后我可以对它进行一些操作如下:

height (Leaf a) = 1
height (Branch a l r) = 1 + (max (height l) (height r))

count (Leaf a) = 1
count (Branch a l r) = 1 + (count l) + (count r) 

我知道 Python 在 Haskell 中没有 data 的等价物。如果有,请告知。

那么,在Python中如何定义一棵二叉树,以及如何在其中实现以上两个功能呢?

【问题讨论】:

  • Haskell 定义不是很好,因为一个节点不能只有一个孩子。因此,例如,您不能创建包含两个 a 值的树。
  • 如果我将EmptyNode 作为另一个构造函数添加到Bint 定义中,就可以做到这一点。

标签: python haskell data-structures constructor


【解决方案1】:

我将在这里寻找与 Haskell 非常相似的函数式编程。从某种意义上说,这不是很“pythonic”。特别是,它不是面向对象的。不过,它仍然有用且干净。

数据类型是一个类。具有多个数据构造函数的数据类型是一个包含有关其构造方式的额外信息的类。当然,它需要一些数据。使用构造函数确保所有树都是合法的:

class BinTree (object):
    def __init__(self, value=None, left=None, right=None):
        if left == None and right == None and value != None:                
            self.isLeaf = True
            self.value = value
        elif left != None and right != None and value == None:
            self.isLeaf = False
            self.value = (left, right)
        else:
            raise ArgumentError("some help message")

这个构造函数调用起来有点不方便,所以有一些好用的智能构造函数:

def leaf(value):
    return BinTree(value=value)

def branch(left, right):
    return BinTree(left=left, right=right)

我们如何获取这些值?让我们也为此做一些帮助:

def left(tree):
    if tree.isLeaf:
        raise ArgumentError ("tree is leaf")
    else:
        return tree.value[0]

def right(tree):
    if tree.isLeaf:
        raise ArgumentError ("tree is leaf")
    else:
        return tree.value[1]

def value(tree):
    if not tree.isLeaf:
        raise ArgumentError ("tree is branch")
    else:
        return tree.value

就是这样。你得到了一个纯“代数”数据类型,可以用函数访问:

def count(bin_tree):
    if bin_tree.isLeaf:
        return 1
    else:
        return count(left(bin_tree))+count(right(bin_tree))

【讨论】:

  • 这似乎对我有帮助。但是class BinTree (object): 中的这个 2 元组 (left, right) 是什么?这对我来说似乎很神奇。
  • 好吧,也许它应该被两个单独的变量替换。对我来说,让一个实例由 a) 构造函数的信息和 b) 构造函数的有效负载组成似乎是合乎逻辑的。所以我一直使用一个名为value 的字段作为有效负载,无论是叶值还是节点的子节点。这很可能是受到 C union 的启发,尽管无论如何都不用担心 Python 中的内存布局。但是如果你用两个字段替换它,leftright,它的工作原理是一样的。
【解决方案2】:

最接近的可能是带有方法的类:

class Leaf:
    def __init__(self, value):
        self.value = value

    def height(self):
        return 1

    def count(self):
        return 1


class Branch:
    def __init__(self, left, right):
        self.left = left
        self.right = right

    def height(self):
        return 1 + max(self.left.height(), self.right.height())

    def count(self):
        return 1 + self.left.count() + self.right.count()

虽然这有点惯用语并且有其自身的优势,但它也缺乏 Haskell 版本的一些品质。最重要的是,函数定义必须与类一起定义在同一个模块中。您可以使用single-dispatch generic functions 而不是方法来取回它。结果比方法和 Haskell 函数更开放,并且允许在有益的情况下将定义扩展到多个模块。

@singledispatch
def height(_):
    raise NotImplementedError()

@singledispatch
def count(_):
    raise NotImplementedError()

class Leaf:
    def __init__(self, value):
        self.value = value

@height.register(Leaf)
def height_leaf(leaf):
    return 1

@height.register(Leaf)
def count_leaf(leaf):
    return 1    

class Branch:
    def __init__(self, left, right):
        self.left = left
        self.right = right

@height.register(Branch)
def height_branch(b):
    return 1 + max(b.left.height(), b.right.height())

@count.register(Branch)
def count_branch(b):
    return 1 + b.left.count() + b.right.count()

【讨论】:

    【解决方案3】:

    Python 没有 Haskell 的“数据构造函数”概念。您可以像大多数其他 OOP 语言一样创建类。或者,您始终可以使用内置函数来表示您的数据类型,并且只定义处理它们的函数(这是用于在 heapq 内置模块中实现堆的方法)。

    python 和 haskell 之间的差异很大,所以最好避免在 haskell 和 python 的语法/特性之间进行严格的比较,否则你最终会写出非 python 和低效的 python 代码。

    即使python确实有一些函数特性,它不是函数式语言,所以你必须彻底改变程序的范式才能获得可读、pythonic和高效的程序。


    使用类的可能实现可能是:

    class Bint(object):
        def __init__(self, value, left=None, right=None):
            self.a = value
            self.left = left
            self.right = right
    
        def height(self):
            left_height = self.left.height() if self.left else 0
            right_height = self.right.height() if self.right else 0
            return 1 + max(left_height, right_height)
    
        def count(self):
            left_count = self.left.count() if self.left else 0
            right_height = self.right.count() if self.right else 0
            return 1 + left_count + right_count
    

    代码可能会简化一点,为leftright 提供“更智能”的默认值:

    class Nil(object):
        def height(self):
            return 0
        count = height
    
    nil = Nil()
    
    class Bint(object):
        def __init__(self, value, left=nil, right=nil):
            self.value = value
            self.left = left
            self.right = right
        def height(self):
            return 1 + max(self.left.height(), self.right.height())
        def count(self):
            return 1 + self.left.count() + self.right.count()
    

    请注意,这些实现允许节点只有一个孩子。

    但是您没有使用类来定义数据类型。 例如,您可以说Bint 可以是单个元素的列表、根的值或具有三个元素的列表:值、左子元素和右子元素。

    在这种情况下,您可以将函数定义为:

    def height(bint):
        if len(bint) == 1:
            return 1
        return 1 + max(height(bint[1]), height(bint[2]))
    
    def count(bint):
        if len(bint) == 1:
        return 1 + count(bint[1]) + count(bint[2])
    

    另一种方法是使用namedtuples:

    from collections import namedtuple
    
    Leaf = namedtuple('Leaf', 'value')
    Branch = namedtuple('Branch', 'value left right')
    
    def height(bint):
        if len(bint) == 1: # or isinstance(bint, Leaf)
            return 1
        return 1 + max(height(bint.left), height(bint.right))
    
    def count(bint):
        if len(bint) == 1:  # or isinstance(bint, Leaf)
        return 1 + count(bint.left) + count(bint.right)
    

    【讨论】:

      【解决方案4】:

      距上次更新已有五年,但这是我的答案。

      data Tree a = Leaf a | Branch (Tree a) (Tree a) deriving (Show) 制作成python...

      没有类型检查(更像python)

      class Tree:
          def __init__(self):
              pass
      
          def height(self):
              pass
      
          def count(self):
              pass
      
      class Leaf(Tree):
          def __init__(self, value):
              self.value = value
      
          def __str__(self):
              return("Leaf " + str(self.value))
      
          def height(self):
              return 1
      
          def count(self):
              return 1
      
      class Branch(Tree):
          def __init__(self, left, right):
              if isinstance(left, Tree) and isinstance(right, Tree):
                  self.left = left
                  self.right = right
              else:
                  raise ValueError
      
          def __str__(self):
              return("Branch (" + str(self.left) + " " +
                     str(self.right) + ")")
      
          def height(self):
              return 1 + max(self.left.height(), self.right.height())
      
          def count(self):
              return 1 + self.left.count() + self.right.count()
      
      
      #usage : Branch(Leaf(5), Branch(Leaf(3), Leaf(2)))
      

      带类型检查(更像是haskell)

      height, count Tree 类中的方法

      class Tree:
          def __init__(self, tree, type_):
              def typecheck(subtree):
                  if isinstance(subtree, Leaf):
                      if not isinstance(subtree.value, type_):
                          print(subtree.value)
                          raise ValueError
                  elif isinstance(subtree, Branch):
                      typecheck(subtree.left)
                      typecheck(subtree.right)
                  else:
                      raise ValueError
              typecheck(tree)
              self.tree = tree
              self.type_ = type_
      
          def __str__(self):
              return ("Tree " + self.type_.__name__ + "\n" + str(self.tree))
      
          def height(self):
              if isinstance(self, Leaf):
                  return 1
              elif isinstance(self, Branch):
                  return 1 + max(self.left.height(), self.right.height())
              else:
                  return self.tree.height()
      
          def count(self):
              if isinstance(self, Leaf):
                  return 1
              elif isinstance(self, Branch):
                  return 1 + self.left.count() + self.right.count()
              else:
                  return self.tree.count()
      
      
      class Leaf(Tree):
          def __init__(self, value):
                  self.value = value
      
          def __str__(self):
                  return("Leaf " + str(self.value))
      
      
      class Branch(Tree):
          def __init__(self, left, right):
              if isinstance(left, Tree) and isinstance(right, Tree):
                  self.left = left
                  self.right = right
              else:
                  raise ValueError
      
          def __str__(self):
              return("Branch (" + str(self.left) + " " +
                     str(self.right) + ")")
      
      
      #usage tree1 = Tree(Branch(Leaf(5), Branch(Leaf(3), Leaf(2))), int)
      #usage tree1.height() -> 3
      #usage tree1.count() -> 5
      

      height, count Leaf 和 Branch 类的方法

      class Tree:
          def __init__(self, tree, type_):
              def typecheck(subtree):
                  if isinstance(subtree, Leaf):
                      if not isinstance(subtree.value, type_):
                          print(subtree.value)
                          raise ValueError
                  elif isinstance(subtree, Branch):
                      typecheck(subtree.left)
                      typecheck(subtree.right)
                  else:
                      raise ValueError
              typecheck(tree)
              self.tree = tree
              self.type_ = type_
      
          def __str__(self):
              return ("Tree " + self.type_.__name__ + "\n" + str(self.tree))
      
          def height(self):
              return self.tree.height()
      
          def count(self):
              return self.tree.count()
      
      
      class Leaf(Tree):
          def __init__(self, value):
                  self.value = value
      
          def __str__(self):
                  return("Leaf " + str(self.value))
      
          def height(self):
              return 1
      
          def count(self):
              return 1
      
      
      class Branch(Tree):
          def __init__(self, left, right):
              if isinstance(left, Tree) and isinstance(right, Tree):
                  self.left = left
                  self.right = right
              else:
                  raise ValueError
      
          def __str__(self):
              return("Branch (" + str(self.left) + " " +
                     str(self.right) + ")")
      
          def height(self):
              return 1 + max(self.left.height(), self.right.height())
      
          def count(self):
              return 1 + self.left.count() + self.right.count()
      
      #usage Tree(Branch(Leaf(5), Branch(Leaf(3), Leaf(2))), int)
      #usage tree1.height() -> 3
      #usage tree1.count() -> 5
      

      height, count 类外的方法(最像haskell)

      class Tree:
          def __init__(self, tree, type_):
              def typecheck(subtree):
                  if isinstance(subtree, Leaf):
                      if not isinstance(subtree.value, type_):
                          print(subtree.value)
                          raise ValueError
                  elif isinstance(subtree, Branch):
                      typecheck(subtree.left)
                      typecheck(subtree.right)
                  else:
                      raise ValueError
              typecheck(tree)
              self.tree = tree
              self.type_ = type_
      
          def __str__(self):
              return ("Tree " + self.type_.__name__ + "\n" + str(self.tree))
      
      
      class Leaf(Tree):
          def __init__(self, value):
                  self.value = value
      
          def __str__(self):
                  return("Leaf " + str(self.value))
      
      
      class Branch(Tree):
          def __init__(self, left, right):
              if isinstance(left, Tree) and isinstance(right, Tree):
                  self.left = left
                  self.right = right
              else:
                  raise ValueError
      
          def __str__(self):
              return("Branch (" + str(self.left) + " " +
                     str(self.right) + ")")
      
      def height(tree):
          if not isinstance(tree, Tree):
              raise ValueError
          if isinstance(tree, Leaf):
              return 1
          elif isinstance(tree, Branch):
              return 1 + max(height(tree.left), height(tree.right))
          else:
              return height(tree.tree)
      
      def count(tree):
          if not isinstance(tree, Tree):
              raise ValueError
          if isinstance(tree, Leaf):
              return 1
          elif isinstance(tree, Branch):
              return 1 + count(tree.left) + count(tree.right)
          else:
              return count(tree.tree)
      
      #usage tree1 = Tree(Branch(Leaf(5), Branch(Leaf(3), Leaf(2))), int)
      #usage height(tree1) -> 3
      #usage count(tree1) -> 5
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-08-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-02-22
        • 1970-01-01
        相关资源
        最近更新 更多