Syntax 为列表生成表达式选择语法

Syntax 为列表生成表达式选择语法,syntax,functional-programming,programming-languages,generator,language-design,Syntax,Functional Programming,Programming Languages,Generator,Language Design,C#具有生成器函数,其语法如下: IEnumerable<int> GetNats(int max) { for (int i=0; i < max; ++i) yield return i; } 此外,匿名函数如下所示: var myFunc = function(int x) { return x + 1; } 除此之外,我的语言语法的其余部分类似于Java或C。我的语言有一个foreach语句,它与C#非常相似 IEnumerable nat

C#具有生成器函数,其语法如下:

IEnumerable<int> GetNats(int max)
{
   for (int i=0; i < max; ++i)
      yield return i;
}
此外,匿名函数如下所示:

var myFunc = function(int x) { 
  return x + 1; 
}
除此之外,我的语言语法的其余部分类似于Java或C。我的语言有一个
foreach
语句,它与C#非常相似

IEnumerable nats=0…42 还是发电机 IEnumerable nats=收益率0…42 还有一种方法——生成器没有特殊语法;“收益率”声明的存在就是一切。任何带块的语句都可以使用,尽管我希望在实践中只能找到循环:

IEnumerable<int> nats = 
    for (i in range(0,42))
        yield i*2

String phrase = "I want to buy some cheese.";
IEnumerable<int> substrs =
    for (i in 0 .. phrase.length)
        for (j in i .. phrase.length+1)
            yield phrase[i..j]
IEnumerable nats=
对于(i在(0,42)范围内)
收益率i*2
字符串短语=“我想买些奶酪。”;
可数子=
for(i在0.短语长度中)
对于(i.短语长度+1中的j)
屈服短语[i..j]
这里我假设右端点不包含在范围内

老实说,当我在Python中看到这一点时,我不禁要问:“代价是什么?”

看看F#做了什么。你能行

seq { seq-expr }    // IEnumerable<T>, a.k.a. seq<T>
[ seq-expr ]        // list<T>
[| seq-expr |]      // array<T>
seq{seq expr}//IEnumerable,又称seq
[seq expr]//列表
[| seq expr |]//数组
其中,seq expr是一种包含大多数语言结构以及“yield”的表单。所以你可以写

seq {
    for i in 0..9 do
        for j in someColl do
            if i <> j then
                yield i*j
}
seq{
因为我在0..9做什么
为了j在someColl做什么
如果我是j那么
收益率i*j
}
F#编译器将此代码转换为IEnumerable的状态机实现(就像C#对迭代器块所做的那样)

我喜欢这种语法,因为它意味着,例如,您可以编写与“立即执行命令”完全相同的代码

0..9 do中的i的

为了j在someColl做什么
如果我是j那么
printfn“%d”i*j

但是将代码包装在seq{}中并使用“yield”,代码就会变成一个懒惰的IEnumerable。

将其作为一个社区wiki,因为没有绝对的答案。在您的语言中,“foreach”是循环集合的关键字吗?如果是这样,那么“witheach”就成了一个自然的选择。根据建议,将其加入到社区维基中@是的,foreach语句看起来就像C#。很好。我把这个例子改得不那么愚蠢了。:-)哦,这里还有第二点:不仅仅是这个例子太琐碎了,而且“a..b”对于范围来说是更好的语法。很好,你是从范围文字构造可枚举的。我想没关系。ienumerable nats=0…42 int[]nats=0…42文字的表示方式取决于变量的类型我非常喜欢这种类型!你预计要花多少钱?我认为任何潜在的性能问题都可以通过一个足够聪明的编译器来消除,而不会对程序本身造成任何损失。我想知道它给编译步骤增加了什么额外的复杂性,因为Python无法在不检查其解析树的情况下判断函数是生成器,尽管这可能不会太糟糕,这取决于您的解析器。您可以将yield语句的节点标记为“generator”节点,然后将该标记传播到语句的根(并非在所有解析器中传播都很容易)。这是一种完全模糊的感觉,没有任何证据支持。我真的很感激在讨论中引入了F#观点。然而,我不理解“seq{…}”添加了什么语义,因为已经有了yield语句。语言不能仅仅推断“for…block”是一个生成器表达式,因为存在一个yield吗?想象一下,第一个“for”前面的行表示“printfn”开始枚举“。如果它包含在seq{}中,那么每次有人迭代这个可枚举项时,副作用都会在生成第一个元素之前发生。如果打印在seq{}之外,则在创建可枚举项之前打印它。因此,在本例中,seq卷曲分隔了延迟到可枚举中的表达式。这只有在我们谈到有副作用的表达(比如printf)时才是一个明显的区别。谢谢你的解释。如果我设计F#,我会让任何语句都是表达式,只要它包含一个收益率。然后可以选择使用seq{}。除非,这就是它的工作原理,我误解了一些东西。
IEnumerable<int> nats = 
    for (i in range(0,42))
        yield i*2

String phrase = "I want to buy some cheese.";
IEnumerable<int> substrs =
    for (i in 0 .. phrase.length)
        for (j in i .. phrase.length+1)
            yield phrase[i..j]
seq { seq-expr }    // IEnumerable<T>, a.k.a. seq<T>
[ seq-expr ]        // list<T>
[| seq-expr |]      // array<T>
seq {
    for i in 0..9 do
        for j in someColl do
            if i <> j then
                yield i*j
}
    for i in 0..9 do
        for j in someColl do
            if i <> j then
                printfn "%d" i*j