SICP Chapter #03 Examples in Oz % Functions defined in previous chapters fun {Gcd A B} if B == 0 then A else {Gcd B (A mod B)} end end fun {Square X} X * X end fun {Average X Y} (X + Y) / 2.0 end fun {HasNoDivisors N C} case C of 1 then true else if N mod C == 0 then false else {HasNoDivisors N C-1} end end end fun {IsPrime N} {HasNoDivisors N N-1} end fun {EnumerateInterval Low High} if Low > High then nil else Low | {EnumerateInterval Low+1 High} end end % 3.1.1 - Assignment and Local State - Local State Variables Balance = {NewCell 100} proc {Withdraw Amount} if @Balance >= Amount then Balance := @Balance - Amount else raise insufficientFunds(@Balance) end end end {Withdraw 25} {Withdraw 25} try {Withdraw 60} catch insufficientFunds(B) then skip end {Withdraw 15} {Browse @Balance} local Balance = {NewCell 100} in proc {NewWithdraw Amount} if @Balance >= Amount then Balance := @Balance - Amount else raise insufficientFunds(@Balance) end end end end fun {MakeWithdraw Balance} proc {$ Amount} if @Balance >= Amount then Balance := @Balance - Amount else raise insufficientFunds(@Balance) end end end end W1 = {MakeWithdraw {NewCell 100}} W2 = {MakeWithdraw {NewCell 100}} {W1 50} {W2 70} try {W2 40} catch insufficientFunds(B) then skip end {W1 40} fun {MakeAccount InitBalance} Balance = {NewCell InitBalance} proc {Withdraw Amount} if @Balance >= Amount then Balance := @Balance - Amount else raise insufficientFunds(@Balance) end end end proc {Deposit Amount} Balance := @Balance + Amount end fun {GetBalance} @Balance end in account(withdraw:Withdraw deposit:Deposit balance:GetBalance) end Acc = {MakeAccount 100} {Acc.withdraw 50} try {Acc.withdraw 60} catch insufficientFunds(B) then skip end {Acc.deposit 40} {Acc.withdraw 60} {Browse {Acc.balance}} Acc2 = {MakeAccount 100} % Exercise 3.1 fun {MakeAccumulator Initial} Accumulator = {NewCell Initial} in fun {$ X} Accumulator := @Accumulator + X @Accumulator end end A = {MakeAccumulator 5} {Browse {A 10}} {Browse {A 10}} % Exercise 3.2 fun {MakeMonitored Proc} CallCount = {NewCell 0} in fun {$ M} case M of how_many_calls then @CallCount [] reset_count then CallCount := 0 else CallCount := @CallCount + 1 {Proc M} end end end Sm = {MakeMonitored Sqrt} {Browse {Sm 100.0}} {Browse {Sm 25.0}} {Browse {Sm how_many_calls}} % Exercise 3.3 fun {MakePasswordAccount InitBalance SecretPassword} Balance = {NewCell InitBalance} proc {Withdraw Amount} if @Balance >= Amount then Balance := @Balance - Amount else raise insufficientFunds(@Balance) end end end proc {Deposit Amount} Balance := @Balance + Amount end fun {GetBalance} @Balance end in fun {$ M Password} if Password == SecretPassword then case M of withdraw then Withdraw [] deposit then Deposit [] balance then GetBalance else raise unknownRequest(M) end end else raise passwordInvalid end end end end PswdAcc = {MakePasswordAccount 100 "secret-password"} {{PswdAcc withdraw "secret-password"} 40} try {{PswdAcc withdraw "some-other-password"} 50} catch passwordInvalid then skip end {Browse {{PswdAcc balance "secret-password"}}} % Exercise 3.4 fun {MakePoliceAccount InitBalance SecretPassword} Balance = {NewCell InitBalance} BadPasswordCount = {NewCell 0} proc {Withdraw Amount} if @Balance >= Amount then Balance := @Balance - Amount else raise insufficientFunds(@Balance) end end end proc {Deposit Amount} Balance := @Balance + Amount end fun {GetBalance} @Balance end in fun {$ M Password} if Password == SecretPassword then BadPasswordCount := 0 case M of withdraw then Withdraw [] deposit then Deposit [] balance then GetBalance else raise unknownRequest(M) end end else BadPasswordCount := @BadPasswordCount + 1 if @BadPasswordCount > 7 then raise call_the_cops end else raise passwordInvalid end end end end end % 3.1.2 - Assignment and Local State - The Benefits of Introducing Assignment RandomInit = {NewCell 7} fun {RandUpdate X} A = 27 B = 26 M = 127 in (A*X + B) mod M end fun {Rand} X = RandomInit in X := {RandUpdate @X} end fun {CesaroTest} {Gcd {Rand} {Rand}} == 1 end fun {MonteCarlo Trials Experiment} fun {Iter TrialsRemaining TrialsPassed} if TrialsRemaining == 0 then {IntToFloat TrialsPassed} / {IntToFloat Trials} else if {Experiment} then {Iter TrialsRemaining-1 TrialsPassed+1} else {Iter TrialsRemaining-1 TrialsPassed} end end end in {Iter Trials 0} end fun {EstimatePi Trials} {Sqrt 6.0 / {MonteCarlo Trials CesaroTest}} end {Browse {EstimatePi 10}} % second version (no assignment) fun {RandomGcdTest Trials InitialX} fun {Iter TrialsRemaining TrialsPassed X} X1 = {RandUpdate X} X2 = {RandUpdate X1} in if TrialsRemaining == 0 then {IntToFloat TrialsPassed} / {IntToFloat Trials} else if {Gcd X1 X2} == 1 then {Iter TrialsRemaining-1 TrialsPassed+1 X2} else {Iter TrialsRemaining-1 TrialsPassed X2} end end end in {Iter Trials 0 InitialX} end % Exercise 3.5 {OS.srand 0} fun {RandomInRange Min Max} X = {IntToFloat {OS.rand}} MinOS MaxOS in {OS.randLimits ?MinOS ?MaxOS} Min + X*(Max - Min) / {IntToFloat (MaxOS - MinOS)} end fun {RectArea X1 X2 Y1 Y2} {Abs (X2-X1) * (Y2-Y1)} end fun {EstimateIntegral P X1 X2 Y1 Y2 Trials} fun {IntegralTest} {P {RandomInRange X1 X2} {RandomInRange Y1 Y2}} end in {RectArea X1 X2 Y1 Y2} * {MonteCarlo Trials IntegralTest} end fun {UnitPred X Y} {Square X} + {Square Y} =< 1.0 end fun {EstimatePi_ Trials} {EstimateIntegral UnitPred 1.0 ~1.0 1.0 ~1.0 10000} end {Browse {EstimatePi_ 10}} % Exercise 3.6 fun {RandWithReset Cmd} case Cmd of generate then Rand [] reset then {OS.srand 0} else raise badCommand(Cmd) end end end % 3.1.3 - Assignment and Local State - The Cost of Introducing Assignment fun {MakeSimplifiedWithdraw Balance} fun {$ Amount} Balance := @Balance - Amount @Balance end end W = {MakeSimplifiedWithdraw {NewCell 25}} _ = {W 20} _ = {W 10} fun {MakeDecrementer Balance} fun {$ Amount} Balance - Amount end end D = {MakeDecrementer 25} {Browse {D 20}} {Browse {D 10}} {Browse {{MakeDecrementer 25} 20}} {Browse {fun {$ Amount} 25 - Amount end 20}} {Browse 25 - 20} {Browse {{MakeSimplifiedWithdraw {NewCell 25}} 20}} % Sameness and change D1 = {MakeDecrementer 25} D2 = {MakeDecrementer 25} W3 = {MakeSimplifiedWithdraw {NewCell 25}} W4 = {MakeSimplifiedWithdraw {NewCell 25}} {Browse {W3 20}} {Browse {W3 20}} {Browse {W4 20}} PeterAcc = {MakeAccount 100} PaulAcc = {MakeAccount 100} PeterAcc1 = {MakeAccount 100} PaulAcc1 = PeterAcc1 % Pitfalls of imperative programming fun {Factorial N} fun {Iter Product Counter} if Counter > N then Product else {Iter Counter*Product Counter+1} end end in {Iter 1 1} end fun {Factorial1 N} Product = {NewCell 1} Counter = {NewCell 1} fun {Iter} if @Counter > N then @Product else Product := @Counter * @Product Counter := @Counter + 1 {Iter} end end in {Iter} end {Show {Factorial1 5}} % Exercise 3.7 fun {MakeJoint Acc AccPass NewPass} fun {$ M Password} if Password == NewPass then {Acc M AccPass} else raise badJointPassword end end end end PeterAcc2 = {MakePasswordAccount 100 "open_sesame"} PaulAcc2 = {MakeJoint PeterAcc2 "open_sesame" "rosebud"} {{PeterAcc2 withdraw "open_sesame"} 40} {{PaulAcc2 withdraw "rosebud"} 20} {Browse {{PeterAcc2 balance "open_sesame"}}} {Browse {{PaulAcc2 balance "rosebud"}}} % Exercise 3.8 fun {Fs} State = {NewCell 1} in fun {$ N} State := @State * N @State end end Fa = {Fs} Fb = {Fs} {Browse {Fa 0} + {Fa 1}} {Browse {Fb 1} + {Fb 0}} % 3.2.1 - The Environment Model of Evaluation - The Rules for Evaluation % Same as above % fun {Square X} X * X end Square1 = fun {$ X} X * X end % 3.2.2 - The Environment Model of Evaluation - Applying Simple Procedures % fun {Square X} X * X end fun {SumOfSquares X Y} {Square X} + {Square Y} end fun {F A} {SumOfSquares A+1 A*2} end % Exercise 3.9 fun {Factorial2 N} if N == 1 then 1 else N * {Factorial2 N-1} end end fun {FactIter Product Counter MaxCount} if Counter > MaxCount then Product else {FactIter Counter*Product Counter+1 MaxCount} end end fun {Factorial3 N} {FactIter 1 1 n} end % 3.2.3 - The Environment Model of Evaluation - Frames as Repository of Local State fun {MakeWithdraw1 Balance} proc {$ Amount} if @Balance >= Amount then Balance := @Balance - Amount else raise insufficientFunds(@Balance) end end end end W5 = {MakeWithdraw1 {NewCell 100}} {W5 50} W6 = {MakeWithdraw1 {NewCell 100}} % Exercise 3.10 fun {MakeWithdraw2 InitialAmount} Balance = {NewCell InitialAmount} in proc {$ Amount} if @Balance >= Amount then Balance := @Balance - Amount else raise insufficientFunds(@Balance) end end end end W7 = {MakeWithdraw2 100} {W7 50} W8 = {MakeWithdraw2 100} % 3.2.4 - The Environment Model of Evaluation - Internal Definitions % same as in section 1.1.8 fun {Sqrt_3 X} fun {GoodEnough Guess} {Abs ({Square Guess} - X)} < 0.001 end fun {Improve Guess} {Average Guess X/Guess} end fun {SqrtIter Guess} if {GoodEnough Guess} then Guess else {SqrtIter {Improve Guess}} end end in {SqrtIter 1.0} end % Exercise 3.11 fun {MakeAccount1 InitBalance} Balance = {NewCell InitBalance} proc {Withdraw Amount} if @Balance >= Amount then Balance := @Balance - Amount else raise insufficientFunds(@Balance) end end end proc {Deposit Amount} Balance := @Balance + Amount end fun {GetBalance} @Balance end in account(withdraw:Withdraw deposit:Deposit balance:GetBalance) end Acc3 = {MakeAccount 50} {Acc3.deposit 40} {Acc3.withdraw 60} Acc4 = {MakeAccount 100} % 3.3.1 - Modeling with Mutable Data - Mutable List Structure fun {MCONS X Y} mcons({NewCell X} {NewCell Y}) end fun {MCAR mcons(X _)} @X end fun {MCDR mcons(_ Xs)} @Xs end fun {MCAR_Cell mcons(X _)} X end fun {MCDR_Cell mcons(_ Xs)} Xs end proc {SetMCAR mcons(X _) Y} X := Y end proc {SetMCDR mcons(_ Xs) Ys} Xs := Ys end % Exercise 3.12 fun {MAppend Xs Ys} case Xs of mcons(H T) then {MCONS @H {MAppend @T Ys}} [] nil then Ys end end % Exercise 3.12 fun {LastPair Xs} case {MCDR Xs} of nil then Xs [] Tail then {LastPair Tail} end end fun {MAppend1 Xs Ys} {SetMCDR {LastPair Xs} Ys} Xs end fun {MList Xs} {FoldR Xs fun {$ V B} {MCONS V B} end nil} end Xm = {MList [a b]} Ym = {MList [c d]} Zm = {MAppend1 Xm Ym} Wx = {MAppend1 Xm Ym} {Browse Wx} {Browse Xm} % Exercise 3.13 fun {MakeCycle Xs} {SetMCDR {LastPair Xs} Xs} Xs end Z = {MakeCycle {MList [a b]}} % Exercise 3.14 fun {Mystery X} fun {Loop X Y} if X == nil then Y else local Temp in Temp = {MCDR X} {SetMCDR X Y} {Loop Temp X} end end end in {Loop X {NewCell nil}} end Vm = {MList [a b c d]} Wm = {Mystery Vm} % Sharing and identity X = {MList [a b]} Z1 = {MList [X X]} Z2 = {MCONS {MList [a b]} {MList [a b]}} proc {SetToWow X} {SetMCAR {MCAR X} "Wow"} end {Browse Z1} {SetToWow Z1} {Browse Z2} {SetToWow Z2} % Exercise 3.16 fun {IsMPair X} case X of mcons(...) then true else false end end fun {BadCountPairs X} if {Not {IsMPair X}} then 0 else {BadCountPairs {MCAR X}} + {BadCountPairs {MCDR X}} + 1 end end E1 = {MList [a b c]} {Browse {BadCountPairs E1}} {SetMCAR {MCDR E1} {MCDR {MCDR E1}}} {Browse {BadCountPairs E1}} {SetMCAR E1 {MCDR E1}} {Browse {BadCountPairs E1}} % infinite loop %{SetMCAR E1 E1} %{Browse {BadCountPairs E1}} % Exercise 3.17 fun {InList Xs X} if {MCAR Xs} == X then true else case {MCDR Xs} of nil then false [] Tail then {InList Tail X} end end end fun {GoodCountPairs X} D = {NewCell {MList [garbage]}} fun {Iterate X} if {Not {IsMPair X}} then 0 elseif {InList @D X} then 0 else D := {MCONS X @D} {Iterate {MCAR X}} + {Iterate {MCDR X}} + 1 end end in {Iterate X} end E2 = {MList [a b c]} {Browse {GoodCountPairs E2}} {SetMCAR {MCDR E2} {MCDR {MCDR E2}}} {Browse {GoodCountPairs E2}} {SetMCAR E2 {MCDR E2}} {Browse {GoodCountPairs E2}} {SetMCAR E2 E2} {Browse {GoodCountPairs E2}} % Exercise 3.18 fun {HasCycle X} D = {NewCell {MList [garbage]}} fun {Iterate X} if {Not {IsMPair X}} then false elseif {InList @D X} then true else D := {MCONS X @D} {Iterate {MCDR X}} end end in {Iterate X} end E3 = {MList [a b c]} {Browse {HasCycle E3}} {SetMCAR {MCDR E3} {MCDR {MCDR E3}}} {Browse {HasCycle E3}} {SetMCAR E3 {MCDR E3}} {Browse {HasCycle E3}} {SetMCDR E3 E3} {Browse {HasCycle E3}} % Exercise 3.19 fun {HasCycle01Space X} fun {Iterate Slow Fast} if Slow == nil orelse Fast == nil then false elseif Slow == Fast then true elseif {MCDR Slow} == nil orelse {MCDR Fast} == nil then false else {Iterate {MCDR Slow} {MCDR {MCDR Fast}}} end end in if X == nil orelse {MCDR X} == nil then false else {Iterate X {MCDR X}} end end E4 = {MList [a b c]} {Browse {HasCycle01Space E4}} {SetMCAR {MCDR E4} {MCDR {MCDR E4}}} {Browse {HasCycle01Space E4}} {SetMCAR E4 {MCDR E4}} {Browse {HasCycle01Space E4}} {SetMCDR E4 E4} {Browse {HasCycle01Space E4}} % Mutation as assignment fun {MCONS1 X Y} fun {Dispatch M} case M of car then X [] cdr then Y end end in Dispatch end fun {MCAR1 Z} {Z car} end fun {MCDR1 Z} {Z cdr} end fun {MCONS2 A B} X = {NewCell A} Y = {NewCell B} proc {SetX V} X := V end proc {SetY V} Y := V end fun {Dispatch Op} case Op of mcar then @X [] mcdr then @Y [] setmcar then SetX [] setmcdr then SetY end end in Dispatch end fun {MCAR2 Z} {Z mcar} end fun {MCDR2 Z} {Z mcdr} end proc {SetMCAR2 Z V} {{Z setmcar} V} end proc {SetMCDR2 Z V} {{Z setmcdr} V} end % Exercise 3.20 X3 = {MCONS2 1 2} Z3 = {MCONS2 X3 X3} {SetMCAR2 {MCDR2 Z3} 17} {Browse {MCAR2 X3}} % 3.3.2 - Modeling with Mutable Data - Local State Variables fun {FrontPtr A#B} A end fun {RearPtr A#B} B end proc {SetFrontPtr Q=F#R Item} F := Item end proc {SetRearPtr Q=F#R Item} R := Item end fun {EmptyQueue Q} @{FrontPtr Q} == nil end fun {MakeQueue} {NewCell nil}#{NewCell nil} end fun {FrontQueue Q} case @{FrontPtr Q} of node(A _) then A end end proc {InsertQueue Q Item} N = node(Item {NewCell nil}) in case @{FrontPtr Q} of nil then {SetFrontPtr Q N} {SetRearPtr Q N} else case @{RearPtr Q} of node(_ Nxt) then Nxt := N {SetRearPtr Q N} end end end fun {DeleteQueue Q} case @{FrontPtr Q} of node(X Nxt) then {SetFrontPtr Q @Nxt} X end end % Exercise 3.21 fun {QueueToList A#_} case @A of nil then nil [] node(X Y) then X|{QueueToList Y#_} end end proc {PrintQueue Q} {Browse {QueueToList Q}} end Q1 = {MakeQueue} {InsertQueue Q1 a} {InsertQueue Q1 b} {InsertQueue Q1 c} {PrintQueue Q1} {Browse {DeleteQueue Q1}} {Browse {DeleteQueue Q1}} {Browse {DeleteQueue Q1}} % Exercise 3.22 fun {MakeQueue2} Front = {NewCell nil} Rear = {NewCell nil} fun {EmptyQueue} @Front == nil end proc {InsertQueue Item} N = node(Item {NewCell nil}) in case @Front of nil then Front := N Rear := N else case @Rear of node(_ Nxt) then Nxt := N Rear := N end end end fun {DeleteQueue} case @Front of node(X Nxt) then Front := @Nxt X end end fun {QueueToList A} case @A of nil then nil [] node(X Y) then X|{QueueToList Y} end end proc {PrintQueue} {Browse {QueueToList Front}} end in queue(empty_queue: EmptyQueue insert_queue: InsertQueue delete_queue: DeleteQueue print_queue: PrintQueue) end Q2 = {MakeQueue2} {Q2.insert_queue a} {Q2.insert_queue b} {Q2.insert_queue c} {Q2.print_queue} {Browse {Q2.delete_queue}} {Browse {Q2.delete_queue}} {Browse {Q2.delete_queue}} % Exercise 3.23 fun {MakeDeque} Front = {NewCell nil} Rear = {NewCell nil} fun {EmptyDeque} @Front == nil orelse @Rear == nil end proc {FrontInsertDeque Item} N = node(Item {NewCell nil} {NewCell nil}) in case @Front of nil then Front := N Rear := N else case N of node(_ Nxt Prv) then Nxt := @Front end case @Front of node(_ Nxt Prv) then Prv := N end Front := N end end proc {RearInsertDeque Item} N = node(Item {NewCell nil} {NewCell nil}) in if {EmptyDeque} then Front := N Rear := N else case N of node(_ Nxt Prv) then Prv := @Rear end case @Rear of node(_ Nxt Prv) then Nxt := N end Rear := N end end fun {FrontDeleteDeque} if {EmptyDeque} then raise empty end end case @Front of node(X Nxt Prv) then Front := @Nxt Prv := {NewCell nil} X end end fun {RearDeleteDeque} if {EmptyDeque} then raise empty end end case @Rear of node(X Nxt Prv) then Rear := @Prv Nxt := {NewCell nil} X end end fun {DequeToList A} case @A of nil then nil [] node(X Y Z) then X|{DequeToList Z} end end proc {PrintDeque} {Browse {DequeToList Rear}} end in deque(empty_deque: EmptyDeque front_insert_deque: FrontInsertDeque rear_insert_deque: RearInsertDeque front_delete_deque: FrontDeleteDeque rear_delete_deque: RearDeleteDeque print_deque: PrintDeque) end Q3 = {MakeDeque} {Q3.front_insert_deque a} {Q3.front_insert_deque b} {Q3.front_insert_deque c} {Q3.rear_insert_deque d} {Q3.rear_insert_deque e} {Q3.rear_insert_deque f} {Q3.print_deque} {Browse {Q3.rear_delete_deque}} {Browse {Q3.rear_delete_deque}} {Browse {Q3.rear_delete_deque}} {Browse {Q3.front_delete_deque}} {Browse {Q3.front_delete_deque}} {Browse {Q3.front_delete_deque}} {Browse {Q3.empty_deque}} % 3.3.3 - Modeling with Mutable Data - Representing Tables fun {Assoc Key Rec} case Rec of tab(Xs) then {Assoc Key @Xs} [] leaf then leaf [] tree(K V Xs) then if Key == K then Rec else {Assoc Key @Xs} end end end fun {Lookup Key Table} Record = {Assoc Key Table} in case Record of tree(K V _) then some(@V) else none end end proc {Insert Key Value Table} Record = {Assoc Key Table} in case Record of tree(K V _) then V := Value else case Table of tab(Xs) then Xs := tree(Key {NewCell Value} {NewCell @Xs}) end end end fun {MakeTable} tab({NewCell leaf}) end D3 = {MakeTable} {Insert abc 123 D3} {Browse {Lookup abc D3}} % two-dimensional fun {Lookup2 Key1 Key2 Table} Record = {Assoc Key1 Table} in case Record of tree(K1 V _) then {Lookup Key2 @V} else none end end proc {Insert2 Key1 Key2 Value Table} Record = {Assoc Key1 Table} in case Record of tree(K V _) then {Insert Key2 Value @V} else case Table of tab(Xs) then local NewTab = {MakeTable} in {Insert Key2 Value NewTab} Xs := tree(Key1 {NewCell NewTab} {NewCell @Xs}) end end end end D4 = {MakeTable} {Insert2 abc 123 12.3 D4} {Browse {Lookup2 abc 123 D4}} % local tables DICTIONARY2 = functor export get : Get put : Put define fun {MakeTable} tab({NewCell leaf}) end Table = {MakeTable} fun {Assoc Key Rec} case Rec of tab(Xs) then {Assoc Key @Xs} [] leaf then leaf [] tree(K V Xs) then if Key == K then Rec else {Assoc Key @Xs} end end end fun {Lookup Key Table} Record = {Assoc Key Table} in case Record of tree(K V _) then some(@V) else none end end proc {Insert Key Value Table} Record = {Assoc Key Table} in case Record of tree(K V _) then V := Value else case Table of tab(Xs) then Xs := tree(Key {NewCell Value} {NewCell @Xs}) end end end fun {Get Key1 Key2} Record = {Assoc Key1 Table} in case Record of tree(K1 V _) then {Lookup Key2 @V} else none end end proc {Put Key1 Key2 Value} Record = {Assoc Key1 Table} in case Record of tree(K V _) then {Insert Key2 Value @V} else case Table of tab(Xs) then local NewTab = {MakeTable} in {Insert Key2 Value NewTab} Xs := tree(Key1 {NewCell NewTab} {NewCell @Xs}) end end end end end [Dictionary2] = {Module.apply [DICTIONARY2]} {Dictionary2.put abc 123 12.3} {Browse {Dictionary2.get abc 123}} % Exercise 3.24 DICTIONARY3 = functor export init : Init get : Get put : Put define Equals Table = tab({NewCell leaf}) proc {Init Feq} Equals = Feq end fun {Assoc Key Rec} case Rec of tab(Xs) then {Assoc Key @Xs} [] leaf then leaf [] tree(K V Xs) then if {Equals Key K} then Rec else {Assoc Key @Xs} end end end fun {Lookup Key Table} Record = {Assoc Key Table} in case Record of tree(K V _) then some(@V) else none end end proc {Insert Key Value Table} Record = {Assoc Key Table} in case Record of tree(K V _) then V := Value else case Table of tab(Xs) then Xs := tree(Key {NewCell Value} {NewCell @Xs}) end end end fun {Get Key1 Key2} Record = {Assoc Key1 Table} in case Record of tree(K1 V _) then {Lookup Key2 @V} else none end end proc {Put Key1 Key2 Value} Record = {Assoc Key1 Table} in case Record of tree(K V _) then {Insert Key2 Value @V} else case Table of tab(Xs) then local NewTab = {MakeTable} in {Insert Key2 Value NewTab} Xs := tree(Key1 {NewCell NewTab} {NewCell @Xs}) end end end end end [Dictionary3] = {Module.apply [DICTIONARY3]} {Dictionary3.init fun {$ X Y} X == Y end} {Dictionary3.put abc 123 12.3} {Browse {Dictionary3.get abc 123}} % Exercise 3.25 fun {XEquals X Y} case X#Y of (Hx|Tx)#(Hy|Ty) andthen Hx == Hy then {XEquals Tx Ty} else X == Y end end [DictionaryX] = {Module.apply [DICTIONARY3]} {DictionaryX.init XEquals} {DictionaryX.put [abc def] 123 12.3} {Browse {DictionaryX.get [abc def] 123}} % Exercise 3.26 % ToDo - Binary Tree DICTIONARY4 = functor %export % init : Init % get : Get % put : Put define Table = tab({NewCell leaf}) /* fun {Assoc Key Rec} case Rec of tab(Xs) then {Assoc Key @Xs} [] leaf then leaf [] tree(K V L R) then if Key == K then Rec else {Assoc Key @Xs} end end end fun {Lookup Key Table} Record = {Assoc Key Table} in case Record of tree(K V _) then some(@V) else none end end proc {Insert Key Value Table} Record = {Assoc Key Table} in case Record of tree(K V _) then V := Value else case Table of tab(Xs) then Xs := tree(Key {NewCell Value} {NewCell @Xs}) end end end fun {Get Key1 Key2} Record = {Assoc Key1 Table} in case Record of tree(K1 V _) then {Lookup Key2 @V} else none end end proc {Put Key1 Key2 Value} Record = {Assoc Key1 Table} in case Record of tree(K V _) then {Insert Key2 Value @V} else case Table of tab(Xs) then local NewTab = {MakeTable} in {Insert Key2 Value NewTab} Xs := tree(Key1 {NewCell NewTab} {NewCell @Xs}) end end end end */ end [Dictionary4] = {Module.apply [DICTIONARY4]} %{Dictionary4.init fun {$ X Y} X == Y end} %{Dictionary4.put abc 123 12.3} %{Browse {Dictionary4.get abc 123}} {Application.exit 0} % Exercise 3.27 fun {Fib N} case N of 0 then 0 [] 1 then 1 else {Fib N-1} + {Fib N-2} end end local Table = {MakeTable} in fun {Memoize F X} local PreviouslyComputedResult = {Lookup X Table} in case PreviouslyComputedResult of some(Item) then Item [] none then local Result = {F X} in {Insert X Result Table} Result end end end end end fun {MemoFib N} fun {Fib N} case N of 0 then 0 [] 1 then 1 else {MemoFib N-1} + {MemoFib N-2} end end in {Memoize Fib N} end {Browse {MemoFib 10}} % 3.3.4 - Modeling with Mutable Data - A Simulator for Digital Circuits proc {CallEach L} case L of nil then skip [] P|Ps then {P} {CallEach Ps} end end fun {GetSignal Wire} {Wire.get_signal} end proc {SetSignal Wire NewValue} {Wire.set_signal NewValue} end proc {AddAction Wire ActionProcedure} {Wire.add_action ActionProcedure} end fun {MakeWire} SignalValue = {NewCell lo} ActionProcedures = {NewCell nil} proc {SetMySignal NewValue} if {Not @SignalValue == NewValue} then SignalValue := NewValue {CallEach @ActionProcedures} else skip end end proc {AcceptActionProcedure Proc} ActionProcedures := Proc | @ActionProcedures end fun {GetSignal} @SignalValue end in wire( get_signal : GetSignal set_signal : SetMySignal add_action : AcceptActionProcedure ) end fun {LogicalNot S} if S == lo then hi else lo end end fun {LogicalAnd S1 S2} if S1 == hi andthen S2 == hi then hi else lo end end fun {LogicalOr S1 S2} if S1 == lo andthen S2 == lo then lo else hi end end fun {MakeTimeSegment Time Queue} time_segment({NewCell Time} Queue) end fun {SegmentTime time_segment(Time Queue)} Time end fun {SegmentQueue time_segment(Time Queue)} Queue end % agenda is a list of time segments fun {MakeAgenda} {MCONS {MakeTimeSegment 0 {MakeQueue}} nil} end fun {CurrentTime Agenda} @{SegmentTime {MCAR Agenda}} end fun {CurrentTimeRef Agenda} {SegmentTime {MCAR Agenda}} end proc {SetCurrentTime Agenda Time} {CurrentTimeRef Agenda} := Time end fun {Segments Agenda} {MCDR Agenda} end proc {SetSegments Agenda Segments} {SetMCDR Agenda Segments} end fun {FirstSegment Agenda} {MCAR {Segments Agenda}} end fun {RestSegments Agenda} {MCDR {Segments Agenda}} end fun {EmptyAgenda Agenda} {Segments Agenda} == nil end fun {FirstAgendaItem Agenda} if {EmptyAgenda Agenda} then raise agenda("Agenda is empty -- FIRST-AGENDA-ITEM") end else local FirstSeg = {FirstSegment Agenda} in {SetCurrentTime Agenda @{SegmentTime FirstSeg}} {FrontQueue {SegmentQueue FirstSeg}} end end end proc {RemoveFirstAgendaItem Agenda} Q = {SegmentQueue {FirstSegment Agenda}} in _ = {DeleteQueue Q} if {EmptyQueue Q} then {SetSegments Agenda {RestSegments Agenda}} else skip end end proc {AddToAgenda Time Action Agenda} fun {BelongsBefore Segments} if Segments == nil then true else Time < @{SegmentTime {MCAR Segments}} end end fun {MakeNewTimeSegment Time Action} Q = {MakeQueue} in {InsertQueue Q Action} {MakeTimeSegment Time Q} end proc {AddToSegments Segments} if @{SegmentTime {MCAR Segments}} == Time then {InsertQueue {SegmentQueue {MCAR Segments}} Action} else local Rest = {MCDR Segments} in if {BelongsBefore Rest} then {SetMCDR Segments {MCONS {MakeNewTimeSegment Time Action} {MCDR Segments}}} else {AddToSegments Rest} end end end end SegmentsX = {Segments Agenda} in if {BelongsBefore SegmentsX} then {SetSegments Agenda {MCONS {MakeNewTimeSegment Time Action} SegmentsX}} else {AddToSegments SegmentsX} end end TheAgenda = {MakeAgenda} proc {AfterDelay Delay Action} {AddToAgenda Delay+{CurrentTime TheAgenda} Action TheAgenda} end InverterDelay = 2 AndGateDelay = 3 OrGateDelay = 5 proc {Inverter Input Output} NewValue = {LogicalNot {GetSignal Input}} proc {InvertInput} {AfterDelay InverterDelay proc {$} {SetSignal Output NewValue} end} end in {AddAction Input InvertInput} end proc {AndGate A1 A2 Output} NewValue = {LogicalAnd {GetSignal A1} {GetSignal A2}} proc {AndActionProcedure} {AfterDelay AndGateDelay proc {$} {SetSignal Output NewValue} end} end in {AddAction A1 AndActionProcedure} {AddAction A2 AndActionProcedure} end proc {OrGate A1 A2 Output} NewValue = {LogicalOr {GetSignal A1} {GetSignal A2}} proc {OrActionProcedure} {AfterDelay OrGateDelay proc {$} {SetSignal Output NewValue} end} end in {AddAction A1 OrActionProcedure} {AddAction A2 OrActionProcedure} end proc {HalfAdder A B S C} D = {MakeWire} E = {MakeWire} in {OrGate A B D} {AndGate A B C} {Inverter C E} {AndGate D E S} end proc {OrGate1 A1 A2 Output} B = {MakeWire} C = {MakeWire} D = {MakeWire} in {Inverter A1 B} {Inverter A2 C} {AndGate B C D} {Inverter D Output} end Aw = {MakeWire} Bw = {MakeWire} Cw = {MakeWire} Dw = {MakeWire} Ew = {MakeWire} Sw = {MakeWire} {OrGate1 Aw Bw Dw} {AndGate Aw Bw Cw} {Inverter Cw Ew} {AndGate Dw Ew Sw} proc {FullAdder A B Cin Sum Cout} S = {MakeWire} C1 = {MakeWire} C2 = {MakeWire} in {HalfAdder B Cin S C1} {HalfAdder A S Sum C2} {OrGate C1 C2 Cout} end proc {Propagate} if {EmptyAgenda TheAgenda} then skip else local FirstItem = {FirstAgendaItem TheAgenda} in {FirstItem} {RemoveFirstAgendaItem TheAgenda} {Propagate} end end end proc {Probe Name Wire} {AddAction Wire proc {$} {Browse Name#" "#{CurrentTime TheAgenda}#" NewValue = "#{GetSignal Wire}} end} end % Sample simulation Input1 = {MakeWire} Input2 = {MakeWire} Sum = {MakeWire} Carry = {MakeWire} {Probe sum Sum} {Probe carry Carry} {HalfAdder Input1 Input2 Sum Carry} {SetSignal Input1 hi} {Propagate} {SetSignal Input2 hi} {Propagate} % Exercise 3.28 % ToDo % Exercise 3.31 % fun {AcceptActionProcedure1 Proc} % ActionProcedures := Proc|ActionProcedures % end % 3.3.5 - Modeling with Mutable Data - Propagation of Constraints proc {InformAboutValue Propagator} {Propagator.process_new_value} end proc {InformAboutNoValue Propagator} {Propagator.process_forget_value} end fun {HasValue Connector} {Connector.has_value} end fun {GetValue Connector} {Connector.get_value} end proc {SetValue Connector NewValue Informant} {Connector.set_value NewValue Informant} end proc {ForgetValue Connector Retractor} {Connector.forget_value Retractor} end proc {Connect Connector NewConstraint} {Connector.connect NewConstraint} end proc {ForEachExcept Except Procedure List} proc {Loop L} case L of nil then skip [] H|T then if H == Except then {Loop T} else {Procedure H} {Loop T} end end end in {Loop List} end fun {MakeConnector} ValueList = {NewCell nil} InformantList = {NewCell nil} Constraints = {NewCell nil} fun {HasValue} {Not @ValueList==nil} end fun {GetValue} @ValueList.1 end fun {Informant} @InformantList.1 end proc {SetValue NewVal Setter} if {Not {HasValue}} then ValueList := [NewVal] InformantList := [Setter] {ForEachExcept Setter InformAboutValue @Constraints} else if {Not {GetValue} == newval} then raise constraint("Contradiction") end else skip end end end proc {ForgetValue Retractor} if {Not @InformantList == nil} andthen Retractor == {Informant} then InformantList := nil ValueList := nil {ForEachExcept Retractor InformAboutNoValue @Constraints} else skip end end proc {Connect NewConstraint} if {Not {Member NewConstraint @Constraints}} then Constraints := NewConstraint | @Constraints else skip end if {HasValue} then {InformAboutValue NewConstraint} else skip end end in connector( has_value : HasValue get_value : GetValue set_value : SetValue forget_value : ForgetValue connect : Connect ) end proc {Adder A1 A2 Sum} Me proc {ProcessNewValue} if {HasValue A1} andthen {HasValue A2} then {SetValue Sum {GetValue A1}+{GetValue A2} Me} else if {HasValue A1} andthen {HasValue Sum} then {SetValue A2 {GetValue Sum}-{GetValue A1} Me} else if {HasValue A2} andthen {HasValue Sum} then {SetValue A1 {GetValue Sum}-{GetValue A2} Me} else skip end end end end proc {ProcessForgetValue} {ForgetValue Sum Me} {ForgetValue A1 Me} {ForgetValue A2 Me} {ProcessNewValue} end in Me = propagator( process_new_value : ProcessNewValue process_forget_value : ProcessForgetValue ) {Connect A1 Me} {Connect A2 Me} {Connect Sum Me} end proc {Multiplier M1 M2 Product} Me proc {ProcessNewValue} if ({HasValue M1} andthen {GetValue M1} == 0.0) orelse ({HasValue M2} andthen {GetValue M2} == 0.0) then {SetValue Product 0.0 Me} else if {HasValue M1} andthen {HasValue M2} then {SetValue Product {GetValue M1}*{GetValue M2} Me} else if {HasValue Product} andthen {HasValue M1} then {SetValue M2 {GetValue Product}/{GetValue M1} Me} else if {HasValue Product} andthen {HasValue M2} then {SetValue M1 {GetValue Product}/{GetValue M2} Me} else skip end end end end end proc {ProcessForgetValue} {ForgetValue Product Me} {ForgetValue M1 Me} {ForgetValue M2 Me} {ProcessNewValue} end in Me = propagator( process_new_value : ProcessNewValue process_forget_value : ProcessForgetValue ) {Connect M1 Me} {Connect M2 Me} {Connect Product Me} end proc {Constant Value Connector} proc {ProcessNewValue} raise constraint("Unknown request -- CONSTANT -- process_new_value") end end fun {ProcessForgetValue} raise constraint("Unknown request -- CONSTANT -- process_forget_value") end end Me = propagator( process_new_value : ProcessNewValue process_forget_value : ProcessForgetValue ) in {Connect Connector Me} {SetValue Connector Value Me} end proc {ProbeConnector Name Connector} proc {PrintProbe Value} {Browse "Probe: "#Name#" = "#Value} end proc {ProcessNewValue} {PrintProbe {GetValue Connector}} end proc {ProcessForgetValue} {Browse "Probe: "#Name#" = ?"} end Me = propagator( process_new_value : ProcessNewValue process_forget_value : ProcessForgetValue ) in {Connect Connector Me} end User = propagator( process_new_value : proc {$} skip end process_forget_value : proc {$} skip end ) proc {CelsiusFahrenheitConverter C F} U = {MakeConnector} V = {MakeConnector} W = {MakeConnector} X = {MakeConnector} Y = {MakeConnector} in {Multiplier C W U} {Multiplier V X U} {Adder V Y F} {Constant 9.0 W} {Constant 5.0 X} {Constant 32.0 Y} end Cx = {MakeConnector} Fx = {MakeConnector} {CelsiusFahrenheitConverter Cx Fx} {ProbeConnector 'Celsius temp' Cx} {ProbeConnector 'Fahrenheit temp' Fx} {SetValue Cx 100.0 User} {ForgetValue Cx User} {SetValue Fx 32.0 User} % Exercise 3.34 proc {Squarer A B} {Multiplier A A B} end % Exercise 3.36 Ay = {MakeConnector} By = {MakeConnector} {SetValue Ay 10 User} % Exercise 3.37 % exercise left to reader to define appropriate functions % proc {CelsiusFahrenheitConverter X} % {CPlus {CTimes {CDivide {Cv 9} {Cv 5}} X} Cv 32} % end % C = {MakeConnector} % F = {CelsiusFahrenheitConverter C} % fun {CPlus X Y} % Z = {MakeConnector() % in % {Adder X Y Z} % Z % end % 3.4.1 - Concurrency: Time Is of the Essence - The Nature of Time in Concurrent Systems Balance1 = {NewCell 100} proc {Withdraw1 Amount} if @Balance1 >= Amount then Balance1 := @Balance1 - Amount else raise insufficientFunds(@Balance1) end end end % Exercise 3.38 Balance1 := @Balance1 + 10 Balance1 := @Balance1 - 20 Balance1 := @Balance1 - (@Balance1 div 2) % 3.4.2 - Concurrency: Time Is of the Essence - Mechanisms for Controlling Concurrency proc {ParallelExecute F1 F2} thread {F1} end thread {F2} end end X4 = {NewCell 10} {ParallelExecute proc {$} X4 := @X4 * @X4 end proc {$} X4 := @X4 + 1 end} % Implementing serializers fun {MakeMutex} {NewLock} end fun {MakeSerializer} Mutex = {MakeMutex} in fun {$ P} lock Mutex then {P} end end end X5 = {NewCell 10} S5 = {MakeSerializer} {ParallelExecute proc {$} _ = {S5 fun {$} X5 := @X5 * @X5 end} end proc {$} _ = {S5 fun {$} X5 := @X5 + 1 end} end} fun {MakeAccount2 InitBalance} Balance = {NewCell InitBalance} proc {Withdraw Amount} if @Balance >= Amount then Balance := @Balance - Amount else raise insufficientFunds(@Balance) end end end proc {Deposit Amount} Balance := @Balance + Amount end fun {GetBalance} @Balance end Lock = {NewLock} in account( withdraw : proc {$ Amount} lock Lock then {Withdraw Amount} end end deposit : proc {$ Amount} lock Lock then {Deposit Amount} end end balance : GetBalance ) end % Exercise 3.39 X6 = {NewCell 10} S6 = {MakeSerializer} {ParallelExecute proc {$} X6 := {S6 fun {$} @X6 * @X6 end} end proc {$} _ = {S6 fun {$} X6 := @X6 + 1 end} end} % Exercise 3.40 X7 = {NewCell 10} {ParallelExecute proc {$} X7 := @X7 * @X7 end proc {$} X7 := @X7 * @X7 * @X7 end} X8 = {NewCell 10} S8 = {MakeSerializer} {ParallelExecute proc {$} _ = {S8 fun {$} X8 := @X8 * @X8 end} end proc {$} _ = {S8 fun {$} X8 := @X8 * @X8 * @X8 end} end} % Exercise 3.41 fun {MakeAccount3 InitBalance} Balance = {NewCell InitBalance} proc {Withdraw Amount} if @Balance >= Amount then Balance := @Balance - Amount else raise insufficientFunds(@Balance) end end end proc {Deposit Amount} Balance := @Balance + Amount end Lock = {NewLock} in account( withdraw : proc {$ Amount} lock Lock then {Withdraw Amount} end end deposit : proc {$ Amount} lock Lock then {Deposit Amount} end end balance : fun {$} lock Lock then @Balance end end ) end % Exercise 3.42 fun {MakeAccount4 InitBalance} Balance = {NewCell InitBalance} proc {Withdraw Amount} if @Balance >= Amount then Balance := @Balance - Amount else raise insufficientFunds(@Balance) end end end proc {Deposit Amount} Balance := @Balance + Amount end fun {GetBalance} @Balance end Lock = {NewLock} proc {ProtectedWithdraw Amount} lock Lock then {Withdraw Amount} end end proc {ProtectedDeposit Amount} lock Lock then {Deposit Amount} end end in account( withdraw : ProtectedWithdraw deposit : ProtectedDeposit balance : GetBalance ) end % Multiple shared resources fun {MakeAccount5 InitBalance} Balance = {NewCell InitBalance} proc {Withdraw Amount} if @Balance >= Amount then Balance := @Balance - Amount else raise insufficientFunds(@Balance) end end end proc {Deposit Amount} Balance := @Balance + Amount end fun {GetBalance} @Balance end Lock = {NewLock} in account( withdraw : Withdraw deposit : Deposit balance : GetBalance serializer : Lock ) end fun {ExchangeX Account1 Account2} Difference = {Account1.balance} - {Account2.balance} in {Account1.withdraw Difference} {Account2.deposit Difference} Difference end fun {Deposit Account Amount} S = Account.serializer D = Account.deposit in lock S then {D Amount} end end fun {SerializedExchange Account1 Account2} Serializer1 = Account1.serializer Serializer2 = Account2.serializer in lock Serializer1 then lock Serializer2 then {ExchangeX Account1 Account2} end end end % Exercise 3.44 fun {Transfer FromAccount ToAccount Amount} {FromAccount.withdraw Amount} {ToAccount.deposit Amount} end % Exercise 3.45 fun {MakeAccount6 InitBalance} proc {Withdraw Amount} if @Balance >= Amount then Balance := @Balance - Amount else raise insufficientFunds(@Balance) end end end proc {Deposit Amount} Balance := @Balance + Amount end fun {GetBalance} @Balance end Lock = {NewLock} in account( withdraw : proc {$ Amount} lock Lock then {Withdraw Amount} end end deposit : proc {$ Amount} lock Lock then {Deposit Amount} end end balance : GetBalance serializer : Lock ) end fun {Deposit1 Account Amount} {Account.deposit Amount} end % 3.5.1 - Streams - Streams Are Delayed Lists fun {SumPrimes A B} fun {Iter Count Accum} if Count > B then Accum else if {IsPrime Count} then {Iter Count+1 Count+Accum} else {Iter Count+1 Accum} end end end in {Iter A 0} end fun {SumPrimes1 A B} {FoldR {Filter {EnumerateInterval A B} IsPrime} Number.'+' 0} end % {Browse {Filter {EnumerateInterval 10000 1000000} IsPrime}.2.1} fun {Force A} {Wait A} A end TheEmptyStream = nil fun {StreamNull Xs} Xs == TheEmptyStream end fun lazy {ConsStream X Xs} X | Xs end fun {StreamCAR Stream} Stream.1 end fun {StreamCDR Stream} {Force Stream.2} end fun {StreamRef0 S N} if N == 0 then S.1 else {StreamRef S.2 N-1} end end StreamRef = List.take fun lazy {StreamMap L Proc} case L of nil then nil [] H|T then {Proc H} | {StreamMap T Proc} end end fun lazy {StreamForEach L Proc} case L of nil then nil [] H|T then {Proc H} {StreamForEach T Proc} end end proc {DisplayLine X} {Browse X} end proc {DisplayStream S} _ = {StreamForEach S DisplayLine} end fun lazy {StreamEnumerateInterval Low High} if Low > High then nil else Low | {StreamEnumerateInterval Low+1 High} end end fun lazy {StreamFilter L Pred} case L of nil then nil [] H|T then if {Pred H} then H | {StreamFilter T Pred} else {StreamFilter T Pred} end end end {Browse {StreamFilter {StreamEnumerateInterval 10000 1000000} IsPrime}.2.1} fun {MemoProc Proc} AlreadyRun = {NewCell false} Result in fun {$} if {Not @AlreadyRun} then AlreadyRun := true Result = {Proc} Result else Result end end end % Exercise 3.51 fun {ShowX X} {Browse X} X end X9 = {StreamMap {StreamEnumerateInterval 0 10} ShowX} _ = {List.take X9 5} _ = {List.take X9 7} % Exercise 3.52 Sum1 = {NewCell 0} fun {Accum X} Sum1 := @Sum1 + X @Sum1 end Seq = {StreamMap {StreamEnumerateInterval 1 20} Accum} Ya = {StreamFilter Seq IsEven} Za = {StreamFilter Seq fun {$ X} X mod 5 == 0 end} _ = {List.take Ya 7} {DisplayStream Za} % 3.5.2 - Streams - Infinite Streams fun lazy {IntegersStartingFrom N} N | {IntegersStartingFrom N+1} end Integers = {IntegersStartingFrom 1} fun {IsDivisible X Y} X mod Y == 0 end NoSevens = {StreamFilter Integers fun {$ X} {Not {IsDivisible X 7}} end} {Browse {List.take NoSevens 100}} fun lazy {FibGen A B} A | {FibGen B A+B} end Fibs = {FibGen 0 1} fun lazy {Sieve Stream} Stream.1 | {Sieve {StreamFilter Stream.2 fun {$ X} {Not {IsDivisible X Stream.1}} end}} end Primes = {Sieve {IntegersStartingFrom 2}} {Browse {List.take Primes 10}} % Defining streams implicitly fun lazy {OnesGen} 1 | {OnesGen} end Ones = {OnesGen} fun lazy {AddStreams L1 L2} case L1#L2 of (X|Xs)#(Y|Ys) then X+Y | {AddStreams Xs Ys} end end fun lazy {IntegersGen} 1 | {AddStreams Ones {IntegersGen}} end Integers1 = {IntegersGen} fun lazy {FibsGen} 0 | 1 | {AddStreams {FibsGen}.2 {FibsGen}} end Fibs = {FibsGen} {Browse {List.take Fibs 10}} fun {ScaleStream Stream Factor} {StreamMap Stream fun {$ X} X * Factor end} end fun lazy {DoubleGen} 1 | {ScaleStream {DoubleGen} 2} end Double = {DoubleGen} {Browse {List.take Double 10}} fun {PrimesGen} 2 | {StreamFilter {IntegersStartingFrom 3} IsPrime} end Primes1 = {PrimesGen} {Browse {List.take Primes1 10}} fun {IsPrime1 N} fun {Iter Ps} if {Square Ps.1} > N then true else if {IsDivisible N Ps.1} then false else {Iter Ps.2} end end end in {Iter Primes} end % Exercise 3.53 fun lazy {SGen} 1 | {AddStreams {SGen} {SGen}} end S = {SGen} % Exercise 3.56 fun lazy {MergeX L1 L2} case L1#L2 of nil#_ then L2 [] _#nil then L1 else local L1car = L1.1 L2car = L2.1 in if L1car < L2car then L1car | {MergeX L1.2 L2} else if L1car > L2car then L2car | {MergeX L1 L2.2} else L1car | {MergeX L1.2 L2.2} end end end end end % Exercise 3.58 fun lazy {Expand Num Den Radix} ((Num * Radix) div Den) | {Expand ((Num * Radix) mod Den) Den Radix} end % Exercise 3.59 % exercise left to reader to define appropriate functions % fun lazy {ExpSeriesGen} 1 | {IntegrateSeries ExpSeriesGen} end % Gen = {ExpSeriesGen} % 3.5.3 - Streams - Exploiting the Stream Paradigm fun {SqrtImprove Guess X} {Average Guess X/Guess} end fun {SqrtStream X} fun lazy {GuessesGen} 1.0 | {StreamMap {GuessesGen} fun {$ Guess} {SqrtImprove Guess X} end} end in {GuessesGen} end {Browse {List.take {SqrtStream 2.0} 5}} fun lazy {AddStreamsReal L1 L2} case L1#L2 of (X|Xs)#(Y|Ys) then X+Y | {AddStreamsReal Xs Ys} end end fun lazy {PartialSums A} A.1 | {AddStreamsReal {PartialSums A} A.2} end fun {ScaleStreamReal Stream Factor} {StreamMap Stream fun {$ X} X * Factor end} end fun lazy {PiSummands N} 1.0/{IntToFloat N} | {StreamMap {PiSummands N+2} fun {$ X} 0.0 - X end} end fun {PiStreamGen} {ScaleStreamReal {PartialSums {PiSummands 1}} 4.0} end PiStream = {PiStreamGen} {Browse {List.take PiStream 5}} fun lazy {EulerTransform S} S0 = {Nth S 1} S1 = {Nth S 2} S2 = {Nth S 3} in (S2 - {Square S2-S1} / (S0 + (~2.0 * S1) + S2)) | {EulerTransform S.2} end {Browse {List.take {EulerTransform PiStream} 8}} fun lazy {MakeTableau S Transform} S | {MakeTableau {Transform S} Transform} end fun {AcceleratedSequence S Transform} {StreamMap {MakeTableau S Transform} fun {$ L} L.1 end} end {Browse {List.take {AcceleratedSequence PiStream EulerTransform} 8}} % Exercise 3.63 fun lazy {SqrtStream1 X} 1.0 | {StreamMap {SqrtStream1 X} fun {$ Guess} {SqrtImprove Guess X} end} end % Exercise 3.64 % exercise left to reader to define appropriate functions % fun {Sqrt X Tolerance} % {StreamLimit {SqrtStream X} Tolerance} % end % Infinite streams of pairs fun lazy {StreamAppend L1 L2} case L1 of nil then L2 [] H|T then H | {StreamAppend T L2} end end fun lazy {Interleave L1 L2} case L1 of nil then L2 [] H|T then H | {Interleave L2 T} end end fun lazy {Pairs S T} [S.1 T.1] | {Interleave {StreamMap T.2 fun {$ X} [S.1 X] end} {Pairs S.2 T.2}} end {Browse {List.take {Pairs Integers Integers} 10}} IntPairs = {Pairs Integers Integers} fun lazy {SopGen} {StreamFilter IntPairs fun {$ Pair} {IsPrime Pair.1 + Pair.2.1} end} end % Exercise 3.68 fun lazy {Pairs1 S T} {Interleave {StreamMap T fun {$ X} [S.1 X] end} {Pairs S.2 T.2}} end {Browse {List.take {Pairs1 Integers Integers} 10}} % Streams as signals fun {Integral Integrand InitialValue Dt} fun lazy {IntGen} InitialValue | {AddStreamsReal {ScaleStreamReal Integrand Dt} {IntGen}} end in {IntGen} end % Exercise 3.74 % exercise left to reader to define appropriate functions % fun lazy {MakeZeroCrossings InputStream LastValue} % {SignChangeDetector InputStream.1 LastValue} | % {MakeZeroCrossings InputStream.2 InputStream.2} % end % ZeroCrossings = {MakeZeroCrossings SenseData 0} % Exercise 3.75 % exercise left to reader to define appropriate functions % fun lazy make_zero_crossings (input_stream, last_value) = % Avpt = (InputStream.1 + LastValue) / 2.0 % in % {SignChangeDetector Avpt LastValue) | % {MakeZeroCrossings InputStream.2 Avpt} % end % 3.5.4 - Streams - Streams and Delayed Evaluation % Note: I don't know if these are working? fun {Solve1 F Y0 Dt} Dy Y = {Integral Dy Y0 Dt} in Dy = {StreamMap Y F} Y end fun {Integral2 DelayedIntegrand InitialValue Dt} Integrand = {Force DelayedIntegrand} fun lazy {IntGen} InitialValue | {AddStreamsReal {ScaleStreamReal Integrand Dt} {IntGen}} end in {IntGen} end fun {Solve2 F Y0 Dt} Dy Y = {Integral2 Dy Y0 Dt} in Dy = {StreamMap Y F} Y end % {Browse {Force {Nth {Solve1 fun {$ Y} Y end 1.0 0.001} 1000}}} % Exercise 3.77 fun lazy {Integral3 Integrand InitialValue Dt} InitialValue | if Integrand == nil then nil else {Integral Integrand.2 (Dt*Integrand.1)+InitialValue Dt} end end % 3.5.5 - Streams - Modularity of Functional Programs and Modularity of Objects % same as in section 3.1.2 fun {Rand1} X = RandomInit in X := {RandUpdate @X} @X end fun lazy {RandomNumbersGen} @RandomInit | {StreamMap {RandomNumbersGen} RandUpdate} end RandomNumbers = {RandomNumbersGen} fun lazy {MapSuccessivePairs S F} {F S.1 S.2.1} | {MapSuccessivePairs S.2.2 F} end CesaroStream = {MapSuccessivePairs RandomNumbers fun {$ R1 R2} {Gcd R1 R2} == 1 end} fun lazy {MonteCarlo1 ExperimentStream Passed Failed} fun {Next Passed Failed} {IntToFloat Passed} / {IntToFloat Passed+Failed} | {MonteCarlo1 ExperimentStream.2 Passed Failed} end in if ExperimentStream.1 then {Next Passed+1 Failed} else {Next Passed Failed+1} end end Pi = {StreamMap {MonteCarlo1 CesaroStream 0 0} fun {$ P} {Sqrt 6.0/P} end} {Wait {List.take Pi 10}} {Browse Pi} % same as in section 3.1.3 fun {MakeSimplifiedWithdraw1 Balance} fun {$ Amount} Balance := @Balance - Amount @Balance end end fun lazy {StreamWithdraw Balance AmountStream} Balance | {StreamWithdraw Balance-AmountStream.1 AmountStream.2} end |