2010-06-21 20 views
16

一般來說,避免GOTO是一種很好的做法。牢記這一點,我一直在與同事討論這個話題。做Perl循環標籤算作一個GOTO?

考慮下面的代碼:

Line: 
    while(<>) { 
     next Line if (insert logic); 
    } 

是否使用循環標籤算作一個goto?

這裏是perlsyn中的perldoc不得不說:

這裏有一個C程序員會如何編寫了Perl中的一個特定的算法:雖然這裏是如何一

for (my $i = 0; $i < @ary1; $i++) { 
    for (my $j = 0; $j < @ary2; $j++) { 
     if ($ary1[$i] > $ary2[$j]) { 
      last; # can't go to outer :-(
     } 
     $ary1[$i] += $ary2[$j]; 
    } 
    # this is where that last takes me 
} 

Perl程序員更習慣這個成語可能會這樣做:

OUTER: for my $wid (@ary1) { 
    INNER: for my $jet (@ary2) { 
       next OUTER if $wid > $jet; 
       $wid += $jet; 
      } 
     } 

我認爲這不是因爲你明確地告訴一個循環短路並提前,但我的同事不同意,說這只是一個奇特的GOTO,應該避免。 我正在尋找一個引人注目的論據或文件,解釋爲什麼這是或不是一個GOTO。我也會接受一個解釋,說明爲什麼這是或不被認爲是perl中的良好實踐。

+2

Downvoted。我不同意「每個人都應該避免轉向」的教條式方法。 – zvrba 2010-06-21 14:04:53

+14

@zvrba => Upvoted to counter。這不是什麼downvotes的意思... – 2010-06-21 14:23:49

+9

如果他在這裏,我會贊同同事,但就是這樣。 – Ether 2010-06-21 15:11:48

回答

27

Dijkstras的意圖從來沒有像goto的東西被認爲是有害的。就是說,gotos被用作幾乎所有程序流程改變的主要構造的代碼結構將導致我們今天稱之爲意大利麪條代碼。

您應該閱讀原文並記住它是在1968年編寫的,當時標註的跳轉是幾乎所有編程語言的主要流程控制結構。

http://userweb.cs.utexas.edu/users/EWD/ewd02xx/EWD215.PDF

+0

還沒有看過這篇文章之前感謝的閱讀。 – 2010-06-21 14:08:20

+7

+1,自從他做到這一點以來,這個引用就被取消了。這是一個結構化編程的論點(又名:if,else,for,while),而不是使用goto來處理所有事情。 – 2010-06-21 14:27:31

+1

令人驚訝的是,「結構化編程」自此成爲常態,大多數人不知道還有其他的東西。這是一件好事。 :) – 2010-06-21 23:06:10

4

誰在乎是否只要它使代碼更容易理解算作轉到?使用goto通常比if()和循環條件中的一堆額外測試更具可讀性。

+5

@Downvoters:爲什麼這是downvoted?他只是說GOTO有它的位置,並且可以正確使用,是一件好事。只是因爲它被稱爲GOTO而說GOTO是邪惡的,這簡直是愚蠢的。 – codymanix 2010-06-21 15:00:55

+1

因爲它不會在討論中添加任何內容。 *只要它使代碼更容易理解,誰會關心風格。*風格的目標是一種模擬的陳述。 – 2010-06-21 16:55:24

15

GOTO標籤的危險在於它們會創建意大利麪代碼並使其不可讀。在這種情況下,這兩種情況都不會發生。在使用GOTO語句方面有很多有效性,其中很多來自Donald Knuth [article]。

深入研究C和Perl示例之間的差異......如果您考慮C程序在彙編級別發生的情況,它無論如何都會編譯到GOTO。如果您已經完成了任何MIPS或其他彙編編程,那麼您已經看到,大多數這些語言沒有任何循環結構,只有條件分支和無條件分支。

最後歸結爲可讀性和可理解性。通過保持一致,這兩者都得到了巨大的幫助。如果你的公司有一個風格指南,那麼遵循,否則遵循Perl風格指南聽起來對我來說是個好主意。這樣當其他perl開發人員將來加入您的團隊時,他們將能夠立即開始工作並對您的代碼庫感到滿意。

+0

我還沒有看到那篇文章,謝謝你看起來很有趣。 – 2010-06-21 17:30:18

2

我覺得區別是有點模糊,但這裏的怎麼樣(不贊成)goto語句的goto perldoc狀態:

其中goto-LABEL形式發現標有LABEL的語句,並繼續執行那裏。

...

的Perl的作者從來沒有感到有必要使用這種形式的goto的(在Perl,那就是; C是另一回事)。 (不同的是,C不提供名爲環與閉環控制相結合的Perl確實,這取代了後藤的大部分結構化用途的其他語言。)

perlsyn perldoc,但是,這樣說:

只要表達式爲真,while語句就會執行塊。只要表達式爲false,until語句就會執行塊。 LABEL是可選的,如果存在的話,由一個標識符和一個冒號組成。 LABEL標識循環控制語句next,last和redo的循環。如果省略LABEL,則循環控制語句引用最內部的封閉循環。這可能包括在運行時動態回顧您的調用棧以找到LABEL。如果您使用use warnings雜注或-w標誌,這種絕望行爲會觸發警告。

絕望的行爲位對我來說看起來不太好,但我可能會誤解它的含義。

Learning Perl書(第5版,第162頁)有這樣一段話:

當你需要使用一個循環塊,這不是最裏面的一個工作,使用標籤。

...

注意標籤名稱的整個塊;它不會在代碼中標記目標點。 [這不是轉到畢竟。]

這是否有助於清理事情?可能不是...... :-)

+1

「回顧你的調用堆棧」意味着跳出當前的子進入一個有適當循環的調用子進程。無論是否涉及標籤,next/last/redo的這種用法確實不太好。 – ysth 2010-06-21 15:29:07

2

在Perl中標記的循環跳轉是GOTO,與C的breakcontinue一樣多。

+0

雖然這並沒有回答OP的問題的精神。 – Ether 2010-06-21 15:12:39

1

gotos是不好的,因爲它們會造成很難理解的代碼 - 特別是通常所說的「意大利麪代碼」。什麼是難以理解的next Line... ??

你可以稱之爲一個循環「名稱」,它確實有助於強調循環邊界。你不會跳入關於循環的任意點;你將回到循環的頂部。令人遺憾的是,如果它是一個團體或家庭標準,可能沒有什麼可以說服團體認爲它不是一個轉變。我有一位經理絕對堅持認爲三元操作員使事情難以閱讀,並且傾向於使用if-blocks來處理所有事情。我有一個非常好的論點,可以在if-else的條款中做任何事情,但是一個三元組明確表示你正在尋找特定的價值。沒有銷售。

+0

經理在$ var = do {if($ cond){$ x} else {$ y}}上的位置是什麼? – 2011-03-11 05:00:39

+0

@Eric Strom,我不認爲他會喜歡這一點。有趣的是,當他談到三元論時,他也說這是「衆所周知的」。當我搜索到它時,我不僅發現有人在說這些,我所能找到的只是幾十個志同道合的程序員想知道爲什麼有人說三元組很難閱讀,而且很難讀懂它。我本可以向他提供鏈接的分數,而不是讓他感到厭煩。它實際上是Java,除了內部類(有些人也不喜歡)之外,三元語是關於它們的主要內容 - 爲了節省輸入。 – Axeman 2011-03-11 14:55:21

1

這種跳躍是一種有紀律的使用goto樣聲明。所以它比沒有遵守goto的規定更有害。 (作爲kasperjj寫道,「Dijkstras意圖從來沒有說任何類似goto語句被認爲是有害的。」

IMO,這Perl的那種跳躍的甚至更好的設計比C的「破發」和「繼續」,因爲它明確了我們破壞或繼續的循環,並且它使代碼更改變得更加可靠。 (此外,它也允許打破或繼續一個外部循環。)

有些權威人士不喜歡休息/繼續之類的,但在某些時候,在經驗法則和可讀性之間存在權衡,而精心挑選的休息/繼續甚至轉到可能會變得比「政治正確」的代碼更具可讀性。

1

break/last和continue/next ARE gotos。我不明白爲什麼有人會爲了避免使用關鍵字而使用不同的關鍵字來做同樣的事情......

+0

'label'沒有轉到 - 他們甚至沒有真正的控制跳轉。而且,你完成相同任務的不同方式就是發表聲明*我不明白人們爲什麼浪費時間殺死癌症,只要他們殺死了主人,他們都完成了同樣的任務。你的任務就是完成工作。 OP的問題涵蓋了完成任務的最佳方式。 – 2010-06-21 16:56:11

+0

你能否提供一個資源來顯示這些命令語句是GOTO的地方/爲什麼?當然,我對標籤如何與這些陳述相互作用感到好奇,但是如果我們可以說標籤只是這些控制陳述的擴展,並且控制陳述是可以接受的,我可能會承認使用這些標籤是相同的級別作爲轉到。儘管如此,我還沒有相信自己。 – 2010-06-21 17:38:20

+0

循環控制語句在概念上與任意跳轉結構(如goto)有很大不同。在循環是主要控制結構的語言中,使用特別有助於構建高效循環的關鍵字是有意義的。 – 2010-06-22 02:31:29

2

我會這樣回答,但我不確定這是否足夠從別人不同的說:

因爲你可以在當前範圍內只有唯一的移動或者父範圍,所以他們可以很比由goto通常暗示,遵守危險:

if (1) { 
    goto BAR; 
    die 'bar' 
} 
BAR: 

這應該很明顯,但這不會(在這個直接的離子)。的labels

if (0) { 
    BAR: 
    die 'bar' 
} 
goto BAR; 

許多用例來自Goto在他們只是核心流量控制的更明確的變種不同。作出聲明,他們是斷然糟糕的是在暗示:

LOOP: while (1) { 
    next LOOP if /foo; 
} 

是有點差於

while (1) { 
    next if /foo/; 
} 

如果排除的風格,簡直是不合邏輯的。但是,就風格而言,後一種版本更易於閱讀 - 而且它不會讓您不必查找正確命名的標籤。讀者通過next(您正在重新啓動當前範圍中的循環)瞭解更多信息,這樣更好。這裏next FOO

讓我們來看看另一個例子

while (1) { 

    while (1) { 
    last; 
    } 

    stuff; 

} 

航班嗎

FOO: while (1) { 

    BAR: while (1) { 
    next FOO; 
    } 

    stuff; 

} 

在後者的例子中,跳過stuff - 你可能希望如此,但它是壞主意。這意味着程序員已經閱讀了一個父範圍來完成這可能是更好的避免的假設。總之,label沒有goto差,有時它們可​​以簡化代碼;但在大多數情況下應該避免。當我在CPAN上遇到它時,我通常會重寫沒有label的循環。

+0

您對可讀性有何看法?可以說,我確實想要根據BAR循環中的某些條件跳過「stuff」。那麼這意味着我需要添加一個額外的變量,該變量被設置在BAR循環內部並讀入FOO循環內部,以便知道循環跳過「stuff」並完成整個循環。當你需要維護一個特別「羅嗦」的循環時,這會變得更加棘手。您現在必須跟蹤多個位置的邏輯,以確保跳過「內容」的邏輯得到維護。思考? – 2010-06-21 17:29:04

+0

我認爲一般來說,設定一個變量更符合預期;而且,在這種情況下,你違反「最少突擊原則」的可能性要小得多。還有其他方法可以通過通用流量控制來跳過給您的東西。如果'stuff;'取決於循環的內容 - 將它移到循環底部('BAR'),然後將'next'提升爲'last'。這將結束'BAR',並開始下一次'FOO'的迭代。 – 2010-06-21 17:36:42

+0

對不起,你說的話讓我困惑,如果真的會讓我震驚。把最後一個嵌套循環的內部導致外循環迭代?即將最後一個放入BAR會導致東西被跳過?也許我只是誤解了。 – 2010-06-21 18:06:21

3

IMO,你的代碼比較是不公平的。目標是可讀代碼。爲了公平起見,你應該比較一個慣用的Perl嵌套循環和標籤,而不是它們。 C語言的風格和阻塞如果語句添加噪聲,使無法比較的方法。

標籤:

OUTER: for my $wid (@ary1) { 
    INNER: for my $jet (@ary2) { 
       next OUTER if $wid > $jet; 
       $wid += $jet; 
      } 
     } 

無標籤:

for my $wid (@ary1) { 
    for my $jet (@ary2) { 
     last if $wid > $jet; 
     $wid += $jet; 
    } 
} 

我更喜歡標記的版本,因爲它是明確了病情$wid > $jet的效果。如果沒有標籤,你需要記住last在內部循環上運行,當內部循環完成時,我們移動到外部循環中的下一個項目。這不完全是火箭科學,但它是真實的,可證明的,認知開銷。正確使用,標籤使代碼更具可讀性。

更新:

stocherilac問,如果你有嵌套循環後的代碼會發生什麼。這取決於您是要根據內部條件跳過它還是始終執行它。

如果您想跳過外循環中的代碼,那麼標記的代碼將按需要工作。

如果您想確保每次都執行它,您可以使用continue塊。

OUTER: for my $wid (@ary1) { 
    INNER: for my $jet (@ary2) { 
       next OUTER if $wid > $jet; 
       $wid += $jet; 
      } 
     } 
     continue { 
      # This code will execute when next OUTER is called. 
     } 
+0

當嵌套循環後有代碼時會發生什麼? – 2010-06-21 20:08:48

+0

沒錯,但是如果你確實使用'last',你跳過'continue',你又回到了不必要的複雜之地。重新審視你的陳述*「我更喜歡帶標籤的版本,因爲它是明確的」* ......對可能跳過的代碼沒有真正的明確。這不是用'last',這意味着只需明確*我在這裏完成(這個範圍)*。它感覺就像全局變量或「退出」是錯誤的一樣。如果不需要明確指導你的父範圍的操作 - 爲什麼這樣做?只要說完就讓它自然發生。 – 2010-06-24 05:44:12

1

4.4.4。循環控制

我們提到你可以在循環中放一個LABEL來給它一個名字。循環的LABEL標識循環控制操作符的下一個,最後一個和重做的循環。 LABEL將循環命名爲一個整體,而不僅僅是循環的頂部。因此,引用循環的循環控制運算符實際上並不會「循環」循環標籤本身。就計算機而言,標籤可以很容易地放置在循環的最後。但人們喜歡上面標註的東西,出於某種原因。

Programming Perl