我在函数式编程和PLT圈子中多次听到过"enggebras"这个术语,特别是在讨论对象,comonads,镜头等时.谷歌搜索这个术语给出了对这些结构进行数学描述的页面,这对我来说几乎是不可理解的.任何人都可以解释一下代数在编程环境中的意义,它们的意义是什么,以及它们与对象和共同体的关系?
haskell functional-programming scala category-theory recursion-schemes
C ++ 20 引入了“销毁operator delete”:operator delete采用标记类型std::destroying_delete_t参数的新重载。
这到底是什么,什么时候有用?
我想建立一个包含一个共同属性的不同东西的列表,即它们可以变成字符串.面向对象的方法很简单:定义接口Showable并使感兴趣的类实现它.当你不能改变类时,第二点原则上可能是一个问题,但让我们假装情况并非如此.然后你创建一个Showables 列表并用这些类的对象填充它而不会产生任何额外的噪音(例如,通常隐式地进行向上转换).这里给出了Java中的概念证明.
我的问题是我在Haskell中有什么选择?下面我讨论一些我尝试过但并不能让我满意的方法.
方法1:existensials.工作但丑陋.
{-# LANGUAGE ExistentialQuantification #-}
data Showable = forall a. Show a => Sh a
aList :: [Showable]
aList = [Sh (1 :: Int), Sh "abc"]
Run Code Online (Sandbox Code Playgroud)
我这里的主要缺点是Sh填写清单时的必要性.这非常类似于在OO语言中隐式完成的向上操作.
更一般地说,Showable已经在语言Show类型类中的虚拟包装器在我的代码中增加了额外的噪声.不好.
方法2:impredicatives.期望但不起作用.
对我来说这个列表最直接的类型和我真正想要的是:
{-# LANGUAGE ImpredicativeTypes #-}
aList :: [forall a. Show a => a]
aList = [(1 :: Int), "abc"]
Run Code Online (Sandbox Code Playgroud)
除此之外(正如我所听到的)ImpredicativeTypes"最好是脆弱而最糟糕的是"它不会编译:
Couldn't match expected type ‘a’ with actual type ‘Int’
‘a’ …Run Code Online (Sandbox Code Playgroud) 我正在阅读有关列表操作的一些技巧,其中包含以下内容:
Run Code Online (Sandbox Code Playgroud)zipRev xs ys = foldr f id xs snd (ys,[]) where f x k c = k (\((y:ys),r) -> c (ys,(x,y):r))我们在这里可以看到,我们有两个连续的叠放在一起。发生这种情况时,他们通常可以“取消”,如下所示:
Run Code Online (Sandbox Code Playgroud)zipRev xs ys = snd (foldr f (ys,[]) xs) where f x (y:ys,r) = (ys,(x,y):r)
我不明白您如何“取消”堆叠的延续以从顶部的代码块到达底部的代码块。您寻找进行这种转换的方式是什么,为什么会起作用?
我一直在阅读Doets和Eijck撰写的《 Haskell逻辑,数学和编程之路》(2004)。这似乎是一本受人尊敬的书,但是当我声称Haskell是Lisp家族的一员时,我感到震惊。这个准确吗?我将用S表达式,不纯函数和列表作为Lisps的特征,并将其作为唯一的复合数据结构。Haskell没有任何东西。该主张有什么理由?
我对 C++ 相当陌生,当我在构造函数和析构函数的行为中徘徊时,我发现了这个问题:
#include <iostream>
struct Student {
std::string a;
~Student() {
std::cout << "Destructor called\n";
}
} S;
int main() {
std::cout << "Before assigning to S\n";
S = {""};
std::cout << "After assigning to S\n";
}
Run Code Online (Sandbox Code Playgroud)
当我编译上面的代码g++并运行它时,它会打印:
Before assigning to S
Destructor called
After assigning to S
Destructor called
Run Code Online (Sandbox Code Playgroud)
但是当我更改std::string a;为 时const char *a;,它会打印:
Before assigning to S
After assigning to S
Destructor called
Run Code Online (Sandbox Code Playgroud)
谁能解释为什么这一变化使析构函数少运行一次?
作为练习,我正在从头开始为 Haskell 编写解析器。在制作词法分析器时,我注意到Haskell 2010 Report 中的以下规则:
digit ? ascDigit | uniDigit
ascDigit ?0|1| … |9
uniDigit ? any Unicode decimal digit
octit ?0|1| … |7
hexit ? digit |A| … |F|a| … |fdecimal ? digit{digit}
octal ? octit{octit}
hexadecimal ? hexit{hexit}integer ? decimal |
0ooctal |0Ooctal | …
考虑这个 C 代码:
void foo(void);
long bar(long x) {
foo();
return x;
}
Run Code Online (Sandbox Code Playgroud)
当我在 GCC 9.3 上使用-O3或编译它时-Os,我得到这个:
bar:
push r12
mov r12, rdi
call foo
mov rax, r12
pop r12
ret
Run Code Online (Sandbox Code Playgroud)
除了选择rbx而不是r12作为被调用者保存的寄存器之外,clang 的输出是相同的。
但是,我希望/期望看到看起来更像这样的程序集:
bar:
push rdi
call foo
pop rax
ret
Run Code Online (Sandbox Code Playgroud)
由于无论如何您都必须将某些内容推送到堆栈中,因此将您的值推送到那里似乎更短,更简单,并且可能更快,而不是将一些任意的被调用者保存的寄存器值推送到那里,然后将您的值存储在该寄存器中。call foo当你把东西放回去后,反之亦然。
我的组装错了吗?它在某种程度上比弄乱额外的寄存器效率低吗?如果这两个的答案都是“否”,那么为什么 GCC 或 clang 不这样做呢?
编辑:这是一个不太简单的例子,即使变量被有意义地使用,它也会发生:
long foo(long);
long bar(long x) {
return foo(x * x) - x;
}
Run Code Online (Sandbox Code Playgroud)
我明白了:
bar:
push rbx …Run Code Online (Sandbox Code Playgroud) 通常,为了向连接到Linux终端上的标准输入的程序指示EOF,如果我只按Enter键,则需要按Ctrl + D一次,否则按两次.但我注意到patch命令不同.有了它,如果我按下Enter键,我需要按两次Ctrl + D,否则按三次.(cat | patch相反,如果我在输入任何实际输入之前按下Ctrl + D,它就没有这种奇怪之处.)深入研究patch源代码,我追溯到它的方式循环fread.这是一个做同样事情的最小程序:
#include <stdio.h>
int main(void) {
char buf[4096];
size_t charsread;
while((charsread = fread(buf, 1, sizeof(buf), stdin)) != 0) {
printf("Read %zu bytes. EOF: %d. Error: %d.\n", charsread, feof(stdin), ferror(stdin));
}
printf("Read zero bytes. EOF: %d. Error: %d. Exiting.\n", feof(stdin), ferror(stdin));
return 0;
}
Run Code Online (Sandbox Code Playgroud)
在完全按原样编译和运行上述程序时,这是事件的时间表:
fread.fread调用read系统调用.read系统调用返回5.freadread再次呼叫系统呼叫.假设我有一些内联汇编,需要一个特定char的值ah,bh,ch,或dh。我怎么能告诉 GCC 把它放在那里?我没有看到相关的约束来做到这一点,但 GCC 手册说“如果你必须使用特定的寄存器,但你的机器约束没有提供足够的控制来选择你想要的特定寄存器,局部寄存器变量可能会提供一个解决方案",所以我试过:
void f(char x) {
register char y __asm__("ah") = x;
__asm__ __volatile__(
"# my value ended up in %0" :: "a"(y)
);
}
Run Code Online (Sandbox Code Playgroud)
但它没有用。它al改为:
movb 4(%esp), %al
# my value ended up in %al
Run Code Online (Sandbox Code Playgroud)
特定于 x86 的Q约束看起来也很接近我想要的,所以我尝试用它代替a,但结果相同。我还尝试使用更通用的r.
有趣的是,当我编译铿锵而不是GCC(是否带a,Q或r),然后我得到了想要的结果:
movb 4(%esp), %ah
# my value ended up in %ah
Run Code Online (Sandbox Code Playgroud)
我还尝试使用bh、 …