R中的列表理解

首先,我很抱歉在本文中将代码示例显示为屏幕截图。 我无法获得Medium和WordPress正确显示代码。 如果您想复制实际的代码来使用它,可以在这里获取:https://github.com/mailund/dsl/blob/master/R/lcomp/lcomp.R

对! 给它!

如果您熟悉Python编程,则还将使用它来列出理解。 它是语法糖,用于从其他列表(或通常可以迭代的任何结构)中构造列表。 它实际上不是Python的发明-您在许多编程语言中都有它-但是Python可能是其中使用最多的语言,因此您很可能从那里知道它。

列表理解为您提供了语法糖,可以编写结合了过滤和映射的表达式。 您可以通过评估另一个列表中每个元素的表达式来构造一个列表,并将其与要包含在结果中的元素必须为真的谓词组合。 使用它,您可以非常简洁地编写算法。 例如,如果您使用列表推导,则可以在几行中用几行代码来实现Python中的快速排序:

R没有列表理解功能,但是它确实对元编程提供了出色的支持,如果您编写自己的领域特定语言,它将使您能够添加元编程-这将是我即将出版的其中一本书的主题。

我想将其实现为对我要写的东西的测试,所以我在周末玩了一段时间,并提出了一个解决方案,至少可以让我尽可能简单地实现快速排序。蟒蛇:

如果它可以正常工作,则它的工作方式如下:

这是我想出的解决方案。 它不是完美的,但是它是玩它的起点:

我为此使用rlang包,而不是使用原始引号和eval,但是您可以轻松地做到这一点。 我只想借用写下一本书的借口来更熟悉该软件包和整洁的评估,所以这就是我所使用的。

这种实现比Python的列表理解更通用-我可以处理多个列表和多个谓词。 在Python中,您可能会认为您可以处理多个列表,但是您会错的。 您可以编写涉及多个表达式的表达式,但这些表达式只是列表理解的嵌套版本。 您会获得列表的外部产品,而不是内部产品。 通过嵌套解决方案,我也可以做到这一点,但是我可以处理更多事情。

无论如何,让我为您介绍解决方案。

lcomp函数接受任意数量的参数,其中第一个必须是表达式,其余的由“ …”特殊参数捕获。 我使用enquo函数将第一个参数转换为带引号的表达式。 在某种程度上,它的作用类似于quote函数,但它首先替换了用户实际写入表达式中的参数,然后对其进行了引用。 与您使用替代函数相似,但是它可以正确处理环境,因此我们可以在用户编写表达式的上下文中评估表达式。 剩余的元素(可以有任意数量的元素)使用quos函数以类似的方式引用。

我们将表达式放在一边,然后考虑其他参数。 这些将是我们应该迭代的列表以及它们的谓词。

我打算使用该函数的方式,将要求您命名列表-在快速排序示例中查看“ x =…”参数-但您将谓词提供为布尔表达式-在调用中的第三个参数快速排序示例。

命名参数将在表达式的带引号的列表中有一个名称,其他名称则没有,因此我可以使用它来区分提供给函数的列表和它们的谓词。

我在用户使用eval_tidy函数定义它们的上下文中评估列表,而我只是使用UQE函数获得这些谓词的原始引用谓词。

在这些语句之后,我在“列表”中提供了所提供列表的命名值,在“谓词”中使用了没有环境关联的表达式。 后者可能是我必须解决的一个问题,但我会回到正题。

无论如何,我都有列表,因此我应该能够在列表中每个元素的上下文中计算lcomp的第一个参数中的表达式。 这是需要在列表上映射函数的事情,但是首先我们必须创建这样的函数。

我们应该如何评估此函数的主体并不是完全无关紧要的。 显然,函数的主体必须是作为lcomp的第一个参数提供的表达式,但是该表达式可以引用列表中的元素以及调用上下文中的变量。

我选择实现的方式是这样的:

我将函数的主体设置为提供给lcomp函数的原始表达式。 这会丢失有关提供表达式的上下文的所有信息,但是我通过使函数的封闭环境来处理这些信息。 然后,任何未作为函数参数提供的内容都可以通过此环境使用。 对于论点,我只推了一下清单。 这使函数采用正确的参数,并将列表作为默认值。 如果表达式是向量表达式,并且所有参数都是向量,则可以仅从这些默认参数对表达式进行求值(我们可以确保快速排序算法可以提供这种方法),但是如果我们将列表和向量组合在一起,则该表达式将不起作用。该表达式未向量化,因此我将函数映射到列表上。

现在,我具有输出列表的所有值,但是忽略了谓词。 我这样做是因为谓词比较难处理。

不过,我处理谓词的方式与计算值的方式类似。 我创建了对列表中的每个谓词求值的函数,然后将它们“和”在一起以得到布尔向量,可以用来选择应该返回的值。

现在,仅在返回“ keep_index”向量为true的情况下返回值的问题就可以得到:

这确实不是理想的解决方案。 我计算最终结果不需要的值,更糟糕的是,如果某些谓词阻止我评估非法输入的表达式,我可能会引发各种异常。

理想情况下,我应该先计算谓词,然后使用该谓词从列表中挑选出要对表达式求值的元素。 但是到了现在,桌上已经有一瓶酒,晚餐已经准备好了,所以我把它放在一边,以便以后玩。

我计划在我的下一本R书中使列表理解成为一章,因此如果您想了解我的想法,请密切注意。

一旦我写了些东西,我就会把那本书的草稿放到Gumroad上。 像往常一样,如果您早点在那买,就会便宜一些,直到我完成为止,您都可以得到更新。 我将尝试筹集足够的钱来支付副本编辑器的费用,然后再尝试出售它-因为RI中的Functional Data Structures刚赚了12.75美元,不会支付很多页面的编辑费用(它可能会为面包,但不是面包),因此它并没有在那里发生,但是如果您帮助我,您将获得一份精读书。 不过,如果您给我发送意见和建议,如果我设法卖出这本书,我会尽力为您提供印刷版。 不过,我只能得到十份,因此我只能将副本发送给真正有帮助的人。