model範例 - Haskell:非嚴格和懶惰有何不同?




international classification of functioning disability and health (4)

我經常讀到懶惰非嚴格不一樣但我發現很難理解其中的區別。 它們似乎可以互換使用,但我知道它們有不同的含義。 我很感激一些幫助理解差異。

關於這篇文章,我有幾個問題。 我將在本文末尾總結這些問題。 我有幾個示例片段,我沒有測試它們,我只是將它們作為概念呈現。 我添加了引號以避免查找它們。 也許以後會有同樣的問題幫助別人。

非嚴格的定義:

如果函數f應用於非終止表達式,它也無法終止,則稱函數f是嚴格的。 換句話說,如果f bot的值為| ,則f是嚴格的 。 對於大多數編程語言,所有功能都是嚴格的。 但在Haskell中並非如此。 舉個簡單的例子,考慮const1,常量1函數,定義如下:

const1 x = 1

Haskell中const1 bot的值為1.從操作上講,由於const1不“需要”其參數的值,因此它永遠不會嘗試對其進行求值,因此永遠不會陷入非終止計算中。 出於這個原因,非嚴格函數也被稱為“惰性函數”,據說可以“懶惰地”或“按需”來評估它們的參數。

- Haskell的簡要介紹:功能

我真的很喜歡這個定義。 這似乎是我能找到的最好的理解嚴格。 const1 x = 1懶惰嗎?

非嚴格意味著減少(評估的數學術語)從外部進行,

所以如果你有(a +(b c))那麼首先減少+,然後你減少內部(b c)。

- Haskell Wiki:Lavy vs非嚴格

Haskell Wiki真讓我困惑。 我理解他們對訂單說的是什麼,但我沒看到(a+(b*c))如果通過_|_如何非嚴格地評估?

在非嚴格評估中,除非它們實際用於評估函數體,否則不會評估函數的參數。

在教會編碼下,操作員的懶惰評估映射到非嚴格的功能評估; 因此,非嚴格評估通常被稱為“懶惰”。 許多語言中的布爾表達式使用一種稱為短路評估的非嚴格評估形式,一旦可以確定將產生明確的布爾值,評估就會返回 - 例如,在遇到true的析取表達式中,或者一個連接表達式,遇到false,等等。 條件表達式通常也使用延遲評估,一旦明確的分支將導致評估返回。

- 維基百科:評估策略

懶惰Def:

另一方面,懶惰評估意味著僅在需要其結果時評估表達式(注意從“減少”到“評估”的轉變)。 因此,當評估引擎看到一個表達式時,它會構建一個thunk數據結構,其中包含評估表達式所需的任何值,以及指向表達式本身的指針。 當實際需要結果時,評估引擎調用表達式,然後將thunk替換為結果以供將來參考。 ...

顯然,thunk和部分評估表達式之間存在強烈的對應關係。 因此,在大多數情況下,術語“懶惰”和“非嚴格”是同義詞。 但並不完全。

- Haskell Wiki:Lavy vs非嚴格

這似乎是Haskell的具體答案。 我認為懶惰意味著thunks和非嚴格意味著部分評估。 這種比較太簡單了嗎? 懶惰總是意味著thunk和非嚴格總是意味著部分評估。

在編程語言理論中,延遲評估或按需調用1是一種評估策略,它延遲表達式的評估,直到其實際需要的值(非嚴格評估),並且還避免重複評估(共享)。

- 維基百科:懶惰評估

勢在必行的例子

我知道大多數人在學習函數式語言時會忘記命令式編程。 但是,我想知道這些是否屬於非嚴格,懶惰,兩者兼而有之? 至少它會提供熟悉的東西。

短路

f1() || f2()

C#,Python和其他語言“產量”

public static IEnumerable Power(int number, int exponent)
{
    int counter = 0;
    int result = 1;
    while (counter++ < exponent)
    {
        result = result * number;
        yield return result;
    }
}

- MSDN:收益率(c#)

回調

int f1() { return 1;}
int f2() { return 2;}

int lazy(int (*cb1)(), int (*cb2)() , int x) {
    if (x == 0)
        return cb1();
    else
        return cb2();
}

int eager(int e1, int e2, int x) {
    if (x == 0)
         return e1;
    else
         return e2;
}

lazy(f1, f2, x);
eager(f1(), f2(), x);

問題

我知道答案就在我面前,擁有所有這些資源,但我無法理解。 似乎這個定義很容易被忽視,因為暗示或顯而易見。

我知道我有很多問題。 隨意回答您認為相關的任何問題。 我添加了這些問題以供討論。

  • const1 x = 1也懶嗎?
  • 如何從“內向”非嚴格評估? 是因為向內允許減少不必要的表達式,例如const1 x = 1 ? 減少似乎符合懶惰的定義。
  • 懶惰總是意味著thunk非嚴格總是意味著部分評估 ? 這只是一個概括嗎?
  • 以下命令性概念是懶惰,非嚴格,兩者還是兩者都沒有?
    • 短路
    • 使用產量
    • 傳遞回調以延遲或避免執行
  • 懶惰 是非嚴格的子集,反之亦然,或者它們是互斥的。 例如,可能是非嚴格而不是懶惰 ,或懶惰而不是非嚴格的
  • Haskell的非嚴格性是否因懶惰而實現?

謝謝你這麼!


如果我們談論一般的計算機科學術語,那麼“懶惰”和“非嚴格”通常是同義詞 - 它們代表相同的整體思想,它以不同的方式表達自己的不同情況。

然而,在給定的特定專業背景下,他們可能已經獲得了不同的技術含義。 我不認為你可以說任何準確和普遍的關於“懶惰”和“非嚴格”之間的區別可能是在存在差異的情況下。


是的,這裡有一些術語使用不清楚,但是大多數情況下這些術語都是一致的,所以這不是一個問題。

一個主要區別是評估術語時 。 對此有多種策略,範圍從“盡快”到“僅在最後時刻”。 渴望評估這個術語有時用於傾向於前者的策略,而懶惰評估恰當地指的是一系列傾向於後者的策略。 “懶惰評估”與相關策略之間的區別傾向於涉及評估某些事物的結果何時何地被保留,而不是拋在一邊。 Haskell中熟悉的為數據結構分配名稱並將其編入索引的memoization技術就是基於此。 相反,一種簡單地將表達式拼接到一起的語言(如“按名稱調用”評估)可能不支持這種情況。

另一個區別是評估哪些術語 ,從“絕對一切”到“盡可能少”。 由於實際用於計算最終結果的任何值都不能忽略,因此這裡的差異是評估了多少多餘的術語。 除了減少程序必須完成的工作量之外,忽略未使用的術語意味著它們不會產生任何錯誤。 當繪製區別時, 嚴格性指的是評估所考慮的一切的屬性(例如,在嚴格函數的情況下,這意味著它應用的術語。它不一定意味著參數內的子表達式)雖然非嚴格意味著僅評估某些事情(通過推遲評估或完全放棄條款)。

應該很容易看出它們如何以復雜的方式相互作用; 決策根本不是正交的,因為極端往往是不相容的。 例如:

  • 非嚴格的評估排除了一些熱情; 如果你不知道是否需要一個術語,你還不能評估它。

  • 非常嚴格的評估使得非渴望有些無關緊要; 如果您正在評估所有內容,那麼何時這樣做的決定就不那麼重要了。

但是,確實存在替代定義。 例如,至少在Haskell中,“嚴格函數”通常被定義為強制其參數足以使函數評估為| 每當有任何爭論時; 請注意,根據此定義, id是嚴格的(在一個微不足道的意義上),因為強制id x的結果將具有與單獨強制x完全相同的行為。


這開始是一個更新,但它開始變得漫長。

Laziness / Call-by-need是call-by-name的memoized版本,其中,如果評估函數參數,則存儲該值以供後續使用。 在“純粹”(無效)設置中,這會產生與按名稱調用相同的結果; 當函數參數被使用兩次或更多次時,按需調用幾乎總是更快。
勢在必行的例子 - 顯然這是可能的。 有一篇關於懶惰命令語言的有趣文章。 它說有兩種方法。 一個需要閉包,第二個使用圖縮減。 由於C不支持閉包,因此需要將參數顯式傳遞給迭代器。 您可以包裝地圖結構,如果該值不存在,則計算它否則返回值。
注意 :Haskell通過“指向代碼的指針實現這一點,代碼在第一次執行時被替換為值” - luqui。
這是非嚴格的按名稱調用,但有結果的共享/記憶。

Call-By-Name - 在逐個調用的評估中,在調用函數之前不會計算函數的參數 - 而是直接將它們替換為函數體(使用捕獲 - 避免替換),然後將其保留為在它們出現在函數中時進行評估。 如果函數體中沒有使用參數,則永遠不會計算參數; 如果多次使用,則每次出現時都會重新評估。
勢在必行的例子:回調
注意:這是非嚴格的,因為如果不使用它會避免評估。

Non-Strict =在非嚴格評估中,除非實際用於評估函數體,否則不會計算函數的參數。
勢在必行示例 :短路
注意 :_ | _似乎是一種測試函數是否為非嚴格的方法

所以函數可以是非嚴格的但不是懶惰的。 懶惰的函數總是非嚴格的。 Call-By-Need部分由Call-By-Name定義,其部分由Non-Strict定義

摘自“懶惰的命令式語言”

2.1。 非嚴格的語義學VS. 懶惰評估我們必須首先澄清“非嚴格語義”和“懶惰評估”之間的區別。 非嚴格語義是指定表達式在初始操作需要之前不進行求值的語義。 可能存在各種類型的非嚴格語義。 例如,非嚴格的過程調用不會評估參數,直到需要它們的值。 數據構造函數可能具有非嚴格語義,其中復合數據由未評估的部分組合。惰性評估(也稱為延遲評估)是通常用於實現非嚴格語義的技術。 在第4節中,通常用於實現延遲評估的兩種方法非常簡單地概括。

按值調用,通過LAZY調用和按姓名調用“按值調用”是用於具有嚴格語義的過程調用的通用名稱。 在valuelanguages調用中,在進行過程調用之前評估過程調用的每個參數; 然後將值傳遞給過程或封閉表達式。 按值調用的另一個名稱是“急切”評估。按值調用也稱為“應用順序”評估,因為所有參數在函數應用之前都會被評估。“懶惰調用”(使用William Clinger的術語[8] ])是使用非嚴格語義的過程調用的名稱。 在通過惰性過程調用調用的語言中,在被替換到過程體之前不會對參數進行求值。 懶惰評估的調用也稱為“正常順序”評估,因為評估表達式的順序(從最裡面到最裡面,從左到右)。“按名稱調用”是懶惰調用的特定實現,在Algol中使用-60 [18]。 Algol-60的設計者希望將名稱調用參數物理地替換到過程體中,用括號括起來並使用合適的名稱更改以避免衝突,然後再評估主體。

打電話給拉齊VS. 需要調用需要調用是延遲調用的延伸,這是由於觀察到懶惰的評估可以通過記住給定的延遲表達式的值(一旦被強制)來優化,因此如果再次需要則不需要重新計算該值。 因此,通過需求評估調用,通過使用記憶來擴展調用惰性方法以避免重複評估的需要。 弗里德曼和懷斯是最早通過需求評估呼籲的人之一,提出了“自殺性懸浮”,這些“自殺性懸浮”在首次評估時會自我毀滅,取而代之的是他們的價值觀。


非嚴格和懶惰,雖然可以非正式互換,但適用於不同的討論領域。

非嚴格是指semantics :表達式的數學意義。 非嚴格適用的世界沒有功能,內存消耗甚至計算機的運行時間的概念。 它簡單地討論了域映射中哪些類型的值到codomain中的哪種值。 特別是, 嚴格函數必須將值⊥(“底部” - 請參閱上面的語義鏈接以獲取更多相關信息)映射到⊥; 非嚴格的功能是不允許這樣做的。

懶惰是指操作行為:代碼在真實計算機上執行的方式。 大多數程序員都會在操作上考慮程序,所以這可能就是你的想法。 延遲評估是指使用thunks的實現 - 指向代碼的指針,這些代碼在第一次執行時被替換為值。 注意這裡的非語義詞:“指針”,“第一次”,“執行”。

懶惰的評估會產生非嚴格的語義,這就是為什麼概念看起來如此接近。 但正如FUZxxl所指出的那樣,懶惰並不是實現非嚴格語義的唯一方法。

如果您有興趣了解有關此區別的更多信息,我強烈推薦上面的鏈接。 閱讀它是我對計算機程序意義概念的轉折點。





lazy-evaluation