۲۶ - ۱۳اضافه‌کردنِ اکیدی

حالا با اضافه کردنِ اکیدی به تایپِ ‏‎List‎‏ و چندتا از عملیات‌هاش، تغییرِ رفتارش در حضورِ تهی رو بررسی می‌کنیم. این بخش بیشتر به منظورِ نمایش نوشته شده تا به عنوان یه مثالِ عملی.

module StrictTest1 where

data List a =
    Nil
  | Const a (List a) deriving Show

sTake :: Int -> List a -> List a
sTake n _
  | n <= 0 = Nil
sTake n Nil = Nil
sTake n (Cons x xs) =
  (Cons x (sTake (n-1) xs))

twoEls   = Cons 1 (Cons undefined Nil)
oneEl    = sTake 1 twoEls

اسمِ این ماژول خیلی دقیق نیست... اینجا ‏‎List‎‏، درست مثلِ ‏‎[a]‎‏، تعریف شده در ‏‎Prelude‎‏ ِ هسکل، تنبل ِه. تابعِ ‏‎take‎‏ ِ اختصاصیِ خودمون (که اسم‌ش رو ‏‎sTake‎‏ گذاشتیم) هم تنبل ِه.

بیاریم‌ش تو REPL ببینیم:

Prelude> twoEls
Cons 1 (Cons *** Exception: Prelude.undefined

Prelude> oneEl
Cons 1 Nil

حالا ببینیم با اضافه کردنِ اکیدی در جاهای مختلفِ برنامه‌مون، رفتارش چطور تغییر می‌کنه.

اول ‏‎BangPatterns‎‏ رو اضافه می‌کنیم تا راحت‌تر بتونیم جاهایی که اکیدی می‌خوایم رو تعیین کنیم:

{-# LANGUAGE BangPatterns #-}

module StrictTest2 where

data List a =
    Nil
  | Const !a (List a) deriving Show

sTake :: Int -> List a -> List a
sTake n _
  | n <= 0 = Nil
sTake n Nil = Nil
sTake n (Cons x xs) =
  (Cons x (sTake (n-1) xs))

twoEls   = Cons 1 (Cons undefined Nil)
oneEl    = sTake 1 twoEls

اون علامت تعجب رو دیدین؟ خوب توی REPL بارگذاری‌ش کنیم:

Prelude> twoEls
Cons 1 *** Exception: Prelude.undefined

Prelude> oneEl
Cons 1 Nil

یه کم اکیدتر:

{-# LANGUAGE BangPatterns #-}

module StrictTest3 where

data List a =
    Nil
  | Const !a (List a) deriving Show

sTake :: Int -> List a -> List a
sTake n _
  | n <= 0 = Nil
sTake n Nil = Nil
sTake n (Cons x !xs) =
  (Cons x (sTake (n-1) xs))

twoEls = Cons 1 (Cons undefined Nil)
oneEl  = sTake 1 twoEls

threeElements = Cons 2 twoEls
oneElT   = sTake 1 threeElements

به ‏‎xs‎‏ اکیدی اضافه کردیم تا ‏‎sTake‎‏ مابقی لیست رو اجبار کنه:

Prelude> twoEls
Cons 1 *** Exception: Prelude.undefined

Prelude> oneEl
*** Exception: Prelude.undefined

Prelude> threeElements
Cons 2 (Cons 1 *** Exception: Prelude.undefined

Prelude> oneElT
Cons 2 Nil

باز هم اکیدتر:

module StrictTest4 where

data List a =
    Nil
  | Const !a !(List a) deriving Show

sTake :: Int -> List a -> List a
sTake n _
  | n <= 0 = Nil
sTake n Nil = Nil
sTake n (Cons x xs) = (Cons x (sTake (n-1) xs))

twoEls = Cons 1 (Cons undefined Nil)
oneEl  = sTake 1 twoEls

Prelude> twoEls
*** Exception: Prelude.undefined
Prelude> oneEl
*** Exception: Prelude.undefined

پس از این آزمایش‌ها چه نتیجه‌ای می‌گیریم؟

N‏‎Cons‎‏‏‎sTake‎‏
۱‏‎Cons a (List a)‎‏‏‎Cons x xs‎‏
۲‏‎Cons !a (List a)‎‏‏‎Cons x xs‎‏
۳‏‎Cons !a (List a)‎‏‏‎Cons x !xs‎‏
۴‏‎Cons !a !(List a)‎‏‏‎Cons x xs‎‏

اینها آزمایش‌ها بودن. حالا خودِ نتایج:

N‏‎twoEls‎‏‏‎oneEl‎‏
۱‏‎Cons 1 (Cons ***‎‏‏‎Cons 1 Nil‎‏
۲‏‎Cons 1 ***‎‏‏‎Cons 1 Nil‎‏
۳‏‎Cons 1 ***‎‏‏‎***‎‏
۴‏‎***‎‏‏‎***‎‏

تأثیرِ اضافه کردنِ اکیدی در جاهای مختلف روی محاسبات، نسبت به تهی کاملاً مشخصه.