【问题标题】:Python Equivalent of Scheme/Lisp CAR and CDR FunctionsScheme/Lisp CAR 和 CDR 函数的 Python 等效项
【发布时间】:2018-04-29 02:27:54
【问题描述】:

我目前正在尝试在 Python 中实现 fold/reduce,因为我不喜欢 functools 的版本。这自然涉及实现类似 Lisp CDR 函数的东西,因为 Python 似乎没有类似的东西。这是我想尝试的:

def tail(lat):
  # all elements of list except first
  acc = []
  for i in range(1,len(lat)):
    acc = acc + [lat[i]]

这是实现此功能的有效方式吗?我是否缺少某种内置功能?提前致谢!

【问题讨论】:

  • 你为什么不喜欢functools的版本?你想让它被柯里化,所以reduce(f) 返回一个函数,它接受一个可迭代并在它上面减少f?或者是因为它是一个带有可选参数的函数,而不是两个可以交替传递给其他高阶函数的单独函数?还是一个名为“Reduce-O”的超级反派杀死了你的父亲,所以你只能使用名为 fold 的函数? (其中一些比其他更合理,但这不是重点;知道您想要更改的内容可能会更容易提出建议。)
  • 我主要不想使用functools.reduce,因为它需要使用 lambda(或者我错了?)而且 python lambdas 不是很灵活。我想要一个可以使用任何二进制函数的版本,不管它有多复杂。我目前正在尝试实现高斯消除,这基本上是所有列表操作,所以我认为某种折叠会使它更容易。
  • Python 中没有“lambda”之类的东西。 lambda 不是一种不同的函数,它只是定义唯一一种函数的不同方式。和 Python 中几乎所有其他东西一样,reduce 甚至不需要函数;它将采用绑定方法、部分、带有自定义调用方法的您自己的类的实例等。因此,您白费了很多麻烦。
  • 谢谢!以后我会牢记这一点。

标签: python higher-order-functions


【解决方案1】:

“类似于 Lisp CDR 函数的东西”是微不足道的:

acc[1:]

这将比您的尝试快得多,但只是一个常数因素。


但是,首先这样做没有多大意义。 CDR 的全部意义在于,当您的列表是存储在 CONS 单元格中的链表时,从一个单元格到其尾部是一个单一的机器语言操作。但是对于数组(这就是 Python 列表),acc[1:]——或者你尝试编写的更复杂的东西,或者实际上任何可能的实现——分配一个大小为 N-1 的全新数组并复制 N-1 个值.

一遍又一遍地这样做的效率成本(在一个期望它几乎免费的算法中)将是如此巨大,以至于使用acc[1:] 的常数因子加速不太可能几乎足够使其可以接受的改进。

大多数使用 CDR 速度快的算法使用这种切片会变慢,而大多数使用这种切片速度快的算法使用 CDR 会变慢。这就是为什么我们首先拥有多个数据结构:因为它们适用于不同的事物。


如果您想知道对数组进行折叠/归约的最有效方法 - 这是 functools.reduce(以及类似 toolz 等库提供的变体)的方式:只需迭代即可。

而仅仅迭代还有另一个巨大的优势。 Python 不仅有列表,它还有一个称为 iterables 的抽象,其中包括迭代器和其他可以懒惰地生成其内容的类型。如果你向前折叠,你可以利用这种懒惰。 (向后折叠当然会占用线性空间,无论是显式地还是在堆栈上——但它仍然比二次复制要好。)忽略这一事实就达不到目的了。

【讨论】:

    猜你喜欢
    • 2020-03-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-04
    相关资源
    最近更新 更多