简体   繁体   English

Haskell相当于Python中的数据构造函数?

[英]Haskell equivalent of data constructors in Python?

In Haskell, I can define a binary tree as follows: 在Haskell中,我可以按如下方式定义二叉树:

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

then I can some operations on it as follows: 然后我可以对它进行一些操作如下:

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) 

I know Python doesn't has equivalent of data in Haskell. 我知道Python在Haskell中没有相应的data If it has, please tell. 如果有,请告诉我。

So, how does one define a binary tree in Python and how to implement the above two functions in it? 那么,如何在Python中定义二叉树以及如何在其中实现上述两个函数呢?

I am going for a close analogue to Haskell an functional programming here. 我将在这里与Haskell进行类似的功能编程。 This is not very "pythonic" in a sense. 从某种意义上说,这不是非常“pythonic”。 Especially, it's not object oriented. 特别是,它不是面向对象的。 It's still useful and clean, though. 不过,它仍然有用且干净。

A datatype is a class. 数据类型是一个类。 A datatype with multiple data constructors is a class with extra information about how it is constructed. 具有多个数据构造函数的数据类型是一个类,其中包含有关如何构造它的额外信息。 And of course, it needs some data. 当然,它需要一些数据。 Use the constructor to assure that it all trees are legal: 使用构造函数确保所有树都合法:

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")

This constructor is a bit inconvenient to call, so have some smart constructors that are easy to use: 这个构造函数调用有点不方便,所以有一些易于使用的智能构造函数:

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

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

How do we get the values out? 我们如何获得价值? Let's make some helpers for that, too: 让我们为此做一些帮助:

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

That's it. 而已。 You got a pure "algebraic" data type which can be accessed with functions: 你有一个纯粹的“代数”数据类型,可以通过函数访问:

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

The closest thing would probably be classes with methods: 最接近的可能是带方法的类:

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()

While this is somewhat idiomatic and has its own share of upsides, it also lacks some qualities of the Haskell version. 虽然这有些惯用并且有其自身的优势,但它也缺乏Haskell版本的一些特性。 Most importantly, the function definitions have to be defined along with the class, in the same module. 最重要的是,函数定义必须与类一起在同一模块中定义。 You can use single-dispatch generic functions instead of methods to get that back. 您可以使用单调度泛型函数而不是方法来获取它。 The result is more open-world-y than both methods and Haskell functions, and allows spreading the definition across multiple modules when beneficial. 结果比方法和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()

Python does not have the concept of "data constructor" as Haskell have. Python没有像Haskell那样的“数据构造函数”的概念。 You can create classes, like most other OOP languages. 您可以像大多数其他OOP语言一样创建类。 Alternatively you can always represent your data type with built-ins and only define the functions to handle them(this is the approach used to implement heaps in the heapq built-in module). 或者,您始终可以使用内置函数表示数据类型,并且只定义处理它们的函数(这是用于在heapq内置模块中实现堆的方法)。

The differences between python and haskell are huge, so it's better to avoid making tight comparisons between syntax/features of haskell and python, otherwise you'll end up writing non-pythonic and inefficient python code. python和haskell之间的区别很大,所以最好避免在haskell和python的语法/特性之间进行严格的比较,否则你最终会编写非pythonic和低效的python代码。

Even if python does have some functional features, it is not a functional language, so you have to completely change the paradigm of your programs to obtain readable, pythonic and efficient programs. 即使python确实具有某些功能特性,它也不是一种功能语言,因此您必须完全改变程序的范例,以获得可读,pythonic和高效的程序。


A possible implementation using classes could be: 使用类的可能实现可以是:

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

The code may be simplified a bit providing a "smarter" default value for left and right : 该代码可以被简化有点提供一个“聪明”的默认值, 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()

Note that these implementations allow nodes with only one children. 请注意,这些实现允许只有一个子节点的节点。

However you do not have to use classes to define a data type. 然而,你不必使用类定义的数据类型。 For example you can say that a Bint can be a list of a single element, the value of the root, or a list with three elements: the value, the left child and the right child. 例如,您可以说Bint可以是单个元素的列表,根的值,也可以是包含三个元素的列表:值,左子项和右子项。

In this case you can define the functions as: 在这种情况下,您可以将函数定义为:

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])

Yet another approach would be to use namedtuple s: 另一种方法是使用namedtuple

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)

Five years since last update, but here is my answer. 自上次更新以来的五年,但这是我的答案。

to make data Tree a = Leaf a | Branch (Tree a) (Tree a) deriving (Show) 使data Tree a = Leaf a | Branch (Tree a) (Tree a) deriving (Show) data Tree a = Leaf a | Branch (Tree a) (Tree a) deriving (Show) to python... data Tree a = Leaf a | Branch (Tree a) (Tree a) deriving (Show)到python ...

Without type check (more like 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)))

With type check (more like haskell) 带类型检查(更像是haskell)

height , count method at Tree class 树类的heightcount方法

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 method at Leaf and Branch class 叶子和分支类的heightcount方法

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 method outside classes (most like haskell) heightcount方法以外的类(最喜欢的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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM