Functional programming D语言中的容器语义与功能风格

Functional programming D语言中的容器语义与功能风格,functional-programming,pass-by-reference,d,pass-by-value,move-semantics,Functional Programming,Pass By Reference,D,Pass By Value,Move Semantics,默认情况下,D中的容器是否具有值或引用语义?如果它们具有引用语义,则这不会从根本上阻碍在D中使用函数式编程风格(与C++11的移动语义相比),如以下(学术)示例中所示: 其中X是一个容器。内置容器 D中唯一的内置容器是片(也称为数组/动态数组)和静态数组。后者具有值语义(与C和C++中不同)——传递时整个数组(浅层)被复制 至于切片,它们是具有间接寻址的值类型,因此可以说它们同时具有值语义和引用语义 将T[]想象成一个struct如下: struct Slice(T) { size_t

默认情况下,D中的容器是否具有值或引用语义?如果它们具有引用语义,则这不会从根本上阻碍在D中使用函数式编程风格(与C++11的移动语义相比),如以下(学术)示例中所示:

其中
X
是一个容器。

内置容器 D中唯一的内置容器是片(也称为数组/动态数组)和静态数组。后者具有值语义(与C和C++中不同)——传递时整个数组(浅层)被复制

至于切片,它们是具有间接寻址的值类型,因此可以说它们同时具有值语义和引用语义

T[]
想象成一个
struct
如下:

struct Slice(T)
{
    size_t length;
    T* ptr;
}
其中,
ptr
是指向片的第一个元素的指针,
length
是片边界内的元素数。您可以访问切片的
.ptr
.length
字段,但尽管数据结构与上述相同,但它实际上是一个内置的编译器,因此没有在任何地方定义(名称
切片
仅用于说明目的)

知道了这一点,您可以看到复制一个切片(分配给另一个变量、传递给函数等)只复制一个长度(无定向-值语义)和一个指针(具有间接-引用语义)

换句话说,切片是数组(位于内存中的任何位置)中的视图,同一数组中可以有多个视图

算法
sort
reverse
from
std.algorithm
适当工作,以满足尽可能多的用户。如果用户希望将结果放入GC分配的切片副本中,并保持原始副本不变,则可以轻松完成(
X.dup
)。如果用户希望将结果放入自定义分配的缓冲区,也可以这样做。最后,如果用户希望就地排序,这是一个选项。无论如何,任何额外的开销都是明确的

然而,需要注意的是,标准库中的大多数算法不需要变异,而是返回延迟评估的范围结果,这是函数编程的特点

用户定义的容器 当涉及到用户定义的容器时,它们可以具有它们想要的任何语义——在D中任何配置都是可能的


std.container
中的容器是引用类型,具有制作副本的
.dup
方法,因此略微模拟切片。

容器是否具有值语义或引用语义完全取决于容器。唯一的内置容器是动态数组、静态数组和关联数组。静态数组具有严格的值语义,因为它们位于堆栈上。关联数组具有严格的引用语义。动态数组大多具有引用语义。它们是不被复制的元素,但它们被复制了,所以它们的语义有点特殊。我建议阅读d数组以了解更多细节

对于正式但不是内置的容器,std.container中的容器都有引用语义,通常情况下,容器应该是这样的,否则效率很低。但是,由于任何人都可以实现自己的容器,任何人都可以创建值类型的容器

但是,像C++一样,D不需要在容器上执行算法,所以就算法而言,容器是否具有引用或值语义是非常不相关的。在C++中,算法对迭代器操作,因此,如果您想对容器排序,则会执行一些类似于<代码>排序(容器。在D中,它们对范围进行操作,因此您需要执行

排序(container[])
。在这两种语言中,您实际上都不会直接对容器进行排序。因此,容器本身是否具有值或引用语义与典型算法无关

<>但是,D比C++更擅长用函数编程,因为范围更适合它。迭代器必须成对传递,这对于链接函数来说不是很好。另一方面,炉灶的链条很好,福布斯利用了这一点。它的主要设计原则之一是,它的大多数函数都在范围内运行,从而允许您在代码中完成通常在unix命令行上使用管道完成的操作,在unix命令行中,您有许多通用工具/函数,它们生成输出,您可以通过管道/传递到其他工具/函数进行操作,允许您链接独立的操作来完成特定于您的需求的事情,而不是依赖于某人编写的程序/函数直接完成您想要的事情。沃尔特·布赖特最近在一次会议上讨论了这个问题

因此,在D中,很容易执行以下操作:

auto stuff = sort(array(take(map!"a % 1000"(rndGen()), 100)));
或者如果您更喜欢UFCS(通用函数调用语法):


在这两种情况下,它都会生成一个0到1000之间的100个随机数的排序列表,代码是一种功能性的样式,其中C++的执行时间要困难得多,而在容器上运行而不是迭代器或范围的库会有更困难的时间。

我不愿意告诉你们这一点,但它是介于两者之间的…有时更像是引用语义,有时更像是值语义:\我不理解对C++11移动语义的引用-移动语义如何与函数式编程相关?(D也有移动语义,顺便说一句)静态数组不仅仅位于堆栈上,任何变量的存储都取决于许多因素,静态数组并不局限于局部函数变量。@jA_cOp它们就像int、float或诸如此类的东西,因为它们在堆栈上,除非您专门将它们放在堆上或放在堆上的对象中,这与
auto stuff = sort(array(take(map!"a % 1000"(rndGen()), 100)));
auto stuff = rndGen().map!"a % 1000"().take(100).array().sort();