4

C#有具有類似語法發生器功能:選擇的列表產生一個語法表達式

IEnumerable<int> GetNats(int max) 
{ 
    for (int i=0; i < max; ++i) 
     yield return i; 
} 

一個功能,我很感興趣,我的編程語言(一個簡單的編程與Java類似,斯卡拉,ActionScript中面向對象,和C#)是生成器表達式。這些本質上是發電機功能的語法糖。

我目前最喜歡的候選人是語法如下:

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

表達range(0, 42)是一個內置的發電機的功能。

所以我的問題是你想用C#/ Java/Scala/ActionScript類型的語言來查看生成器表達式的語法,爲什麼?

可能影響響應的一些因素是,像我的語言中的Scala和ActionScript類型在類型之後聲明。例如:

var myVar : SomeType = initialValue; 

而且匿名函數是這樣的:

var myFunc = function(int x) { 
    return x + 1; 
} 

比我的語言語法的其餘部分類似於Java或C#其他。我的語言有一個foreach聲明,它非常類似於C#。

+1

使這個社區維基 - 因爲沒有絕對的答案。 – 2009-10-24 13:58:33

+0

「foreach」是用於循環使用您的語言的集合的關鍵字嗎?如果是這樣,那麼使「witheach」成爲一種自然選擇。 – outis 2009-10-24 14:14:28

+0

根據建議將其製作成社區wiki。@outis,是的foreach語句看起來就像C#一樣。 – cdiggins 2009-10-24 14:18:32

回答

3

還有Python's方法 - 沒有特殊的發生器語法; 「yield」語句的存在就是它所需要的。可以使用,需要一個塊的任何聲明,但我希望找到只有在實踐中使用的循環:

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] 

這裏我假設正確的終點不包括在範圍內。老實說,當我在Python中看到這個時,我不得不想知道:「花了多少錢?」

+1

的我喜歡一個很多!你期望什麼成本?我認爲任何潛在的性能問題都可以通過一個足夠聰明的編譯器來消除。 – cdiggins 2009-10-24 15:04:23

+1

程序本身沒有成本。我想知道它在編譯步驟中增加了多少額外的複雜性,因爲Python不能檢查函數是一個沒有檢查其分析樹的生成器,儘管它可能不會太糟糕,這取決於您的解析器。您可以將節點標記爲yield語句作爲「生成器」節點,然後將該標記傳播到語句的根目錄(而不是在所有解析器中傳播都很容易)。這是一種完全模糊的感覺,沒有什麼可以支持它。 – outis 2009-10-25 00:51:03

1
 
IEnumerable nats = 0...42 

or for generators 

IEnumerable nats = yield 0...42 
+0

好點。我改變了這個例子不那麼愚蠢。 :-) – cdiggins 2009-10-24 14:19:18

+0

噢,這裏還有第二點:不僅僅是這個例子太簡單了,而且「a..b」是範圍更好的語法。尼斯。 – cdiggins 2009-10-24 14:57:39

+0

你從範圍文字構造枚舉。我認爲沒關係。 IEnumerable的NATS = 0 ... 42 INT [] NATS = 0 ... 42 如何字面將被表示取決於類型變量 – 2009-10-24 15:01:53

2

查看F#的功能。你可以做

seq { seq-expr } // IEnumerable<T>, a.k.a. seq<T> 
[ seq-expr ]  // list<T> 
[| seq-expr |]  // array<T> 

其中SEQ-expr是包含與「產量」沿着大部分語言結構的形式。所以例如您可以編寫

seq { 
    for i in 0..9 do 
     for j in someColl do 
      if i <> j then 
       yield i*j 
} 

F#編譯器將此代碼轉換爲IEnumerable的狀態機實現(就像C#的迭代器塊一樣)。

我喜歡這種語法,因爲它意味着例如你可以寫出完全相同的代碼,你可以寫入「現在做必要的事情」,例如

for i in 0..9 do 
     for j in someColl do 
      if i <> j then 
       printfn "%d" i*j 

但將該代碼包裝在seq {}中並使用'yield',代碼變爲惰性的IEnumerable。

+1

我非常欣賞引入討論的F#視角。但是我不明白「seq {...}」添加了什麼語義,因爲已經有一個yield語句。難道語言只是推斷「for ... block」是一個生成器表達式,因爲存在yield嗎? – cdiggins 2009-10-24 20:14:27

+1

想象一下在第一個'for'之前的行說''printfn'開始枚舉''。如果它包含在seq {}中,那麼每當有人迭代這個枚舉時,在生成第一個元素之前就會發生這種副作用。如果打印位於seq {}之外,則在創建枚舉之前將打印它。因此,在這種情況下,seq curlies將表達式定義爲enumerable。當我們談到帶有副作用的表達式(如printf)時,這隻會是一個明顯的差異。 – Brian 2009-10-24 20:23:19

+0

感謝您的解釋大腦。如果我在設計F#,我會讓任何語句成爲一個表達式,只要它包含一個收益。 seq {}然後可以選擇性地使用。除非,這是它的工作原理,我誤解了一些東西。 – cdiggins 2009-10-25 19:52:09