SICP · Yr ail argraffiad · Scheme
4.2 Amrywiadau ar Scheme — Gwerthuso Diog
Gan fod gennym bellach werthusydd wedi'i fynegi fel rhaglen Lisp, gallwn arbrofi â dewisiadau amgen wrth ddylunio iaith drwy addasu'r gwerthusydd yn unig. Yn wir, dyfeisir ieithoedd newydd yn aml drwy ysgrifennu gwerthusydd yn gyntaf sy'n ymgorffori'r iaith newydd mewn iaith lefel uchel sydd eisoes yn bodoli. Er enghraifft, os dymunwn drafod rhyw agwedd ar newid arfaethedig i Lisp ag aelod arall o gymuned Lisp, gallwn ddarparu gwerthusydd sy'n ymgorffori'r newid. Gall y derbynnydd wedyn arbrofi â'r gwerthusydd newydd ac anfon sylwadau yn ôl ar ffurf addasiadau pellach. Mae'r sail weithredu lefel uchel yn ei gwneud yn haws profi a dadfygio'r gwerthusydd; yn ogystal, mae'r ymgorffori'n galluogi'r dylunydd i gipio235 nodweddion o'r iaith sylfaenol, yn union fel y mae ein gwerthusydd Lisp ymgorfforedig yn defnyddio gweithdrefnau cyntefig a strwythur rheoli'r Lisp sylfaenol. Dim ond yn ddiweddarach (os o gwbl) y mae angen i'r dylunydd fynd i'r drafferth o adeiladu gweithrediad cyflawn mewn iaith lefel isel neu mewn caledwedd. Yn yr adran hon a'r nesaf archwiliwn rai amrywiadau ar Scheme sy'n darparu gallu mynegi ychwanegol sylweddol.
4.2.1Trefn Arferol a Threfn Gymhwysol
Yn 1.1, lle dechreuon ni drafod modelau gwerthuso, nodwyd bod Scheme yn iaith trefn gymhwysol, hynny yw, bod holl argiau gweithdrefnau Scheme yn cael eu gwerthuso pan gymhwysir y weithdrefn. Mewn cyferbyniad, mae ieithoedd trefn arferol yn gohirio gwerthuso argiau gweithdrefnau nes bod angen gwerthoedd gwirioneddol yr argiau. Gelwir gohirio gwerthuso argiau gweithdrefnau tan yr eiliad olaf bosibl (e.e., nes bod gweithrediad cyntefig yn gofyn amdanynt) yn werthuso diog (lazy evaluation).236 Ystyriwch y weithdrefn
(define (try a b)
(if (= a 0) 1 b))Mae gwerthuso (try 0 (/ 1 0)) yn cynhyrchu gwall yn Scheme. Gyda gwerthuso diog, ni fyddai gwall. Byddai gwerthuso'r mynegiad yn dychwelyd 1, gan na fyddai'r arg (/ 1 0) byth yn cael ei werthuso.
Enghraifft sy'n manteisio ar werthuso diog yw'r diffiniad o weithdrefn unless
(define (unless condition
usual-value
exceptional-value)
(if condition
exceptional-value
usual-value))y gellir ei defnyddio mewn mynegiadau megis
(unless (= b 0)
(/ a b)
(begin
(display "exception: returning 0")
0))Ni fydd hyn yn gweithio mewn iaith trefn gymhwysol oherwydd caiff y gwerth arferol a'r gwerth eithriadol ill dau eu gwerthuso cyn galw unless (cymharer ag Ymarfer 1.6). Un fantais i werthuso diog yw y gall rhai gweithdrefnau, megis unless, gyflawni cyfrifiannu defnyddiol hyd yn oed pe bai gwerthuso rhai o'u hargiau'n cynhyrchu gwallau neu'n methu â therfynu.
Os eir i mewn i gorff gweithdrefn cyn gwerthuso arg, dywedwn fod y weithdrefn yn anllym yn yr arg hwnnw. Os gwerthusir yr arg cyn mynd i mewn i gorff y weithdrefn, dywedwn fod y weithdrefn yn llym yn yr arg hwnnw.237 Mewn iaith trefn gymhwysol bur, mae pob gweithdrefn yn llym ym mhob arg. Mewn iaith trefn arferol bur, mae pob gweithdrefn gyfansawdd yn anllym ym mhob arg, a gall gweithdrefnau cyntefig fod yn llym neu'n anllym. Ceir hefyd ieithoedd (gweler Ymarfer 4.31) sy'n rhoi rheolaeth fanwl i raglenwyr dros lymder y gweithdrefnau y maent yn eu diffinio.
Enghraifft drawiadol o weithdrefn y gellir ei gwneud yn anllym yn fuddiol yw cons (neu, yn gyffredinol, bron unrhyw adeiladydd ar gyfer strwythurau data). Gellir cyflawni cyfrifiannu defnyddiol, gan gyfuno elfennau i ffurfio strwythurau data a gweithredu ar y strwythurau data sy'n deillio o hynny, hyd yn oed os nad yw gwerthoedd yr elfennau'n hysbys. Mae'n gwneud synnwyr llwyr, er enghraifft, i gyfrifo hyd rhestr heb wybod gwerthoedd yr elfennau unigol yn y rhestr. Byddwn yn manteisio ar y syniad hwn yn 4.2.3 i weithredu ffrydiau Pennod 3 fel rhestrau wedi'u ffurfio o barau cons anllym.
Ymarfer 4.25: Tybiwch ein bod (mewn Scheme trefn gymhwysol arferol) yn diffinio
unlessfel y dangosir uchod ac yna'n diffiniofactorialyn nhermauunlessfel(define (factorial n) (unless (= n 1) (* n (factorial (- n 1))) 1))Beth sy'n digwydd os ceisiwn werthuso
(factorial 5)? A fydd ein diffiniadau'n gweithio mewn iaith trefn arferol?
Ymarfer 4.26: Mae Ben Bitdiddle ac Alyssa P. Hacker yn anghytuno ynghylch pwysigrwydd gwerthuso diog wrth weithredu pethau megis
unless. Mae Ben yn nodi ei bod yn bosibl gweithreduunlessmewn trefn gymhwysol fel ffurf arbennig. Mae Alyssa yn gwrthddadlau, pe gwneid hynny, mai cystrawen yn unig fyddaiunless, yn hytrach na gweithdrefn y gellid ei defnyddio ar y cyd â gweithdrefnau trefn uwch. Llanwch y manylion ar ddwy ochr y ddadl. Dangoswch sut i weithreduunlessfel mynegiad deilliedig (felcondneulet), a rhowch enghraifft o sefyllfa lle gallai fod yn ddefnyddiol caelunlessar gael fel gweithdrefn, yn hytrach nag fel ffurf arbennig.
4.2.2Dehonglydd â Gwerthuso Diog
Yn yr adran hon byddwn yn gweithredu iaith trefn arferol sydd yr un fath â Scheme ac eithrio bod gweithdrefnau cyfansawdd yn anllym ym mhob arg. Bydd gweithdrefnau cyntefig yn dal yn llym. Nid yw'n anodd addasu gwerthusydd 4.1.1 fel bod yr iaith y mae'n ei dehongli yn ymddwyn fel hyn. Mae bron pob un o'r newidiadau angenrheidiol yn ymwneud â chymhwyso gweithdrefnau.
Y syniad sylfaenol yw bod rhaid i'r dehonglydd, wrth gymhwyso gweithdrefn, benderfynu pa argiau sydd i'w gwerthuso a pha rai sydd i'w gohirio. Ni werthusir yr argiau gohiriedig; yn hytrach, cânt eu trawsnewid yn wrthrychau a elwir yn becynnau gwerthuso gohiriedig (thunks).238 Rhaid i'r pecyn gynnwys yr wybodaeth angenrheidiol i gynhyrchu gwerth yr arg pan fydd ei angen, fel petai wedi'i werthuso ar adeg y cymhwyso. Felly, rhaid i'r pecyn gynnwys mynegiad yr arg a'r amgylchedd lle mae cymhwyso'r weithdrefn yn cael ei werthuso.
Gelwir y broses o werthuso'r mynegiad mewn pecyn gwerthuso gohiriedig yn orfodi gwerthuso.239 Yn gyffredinol, dim ond pan fydd angen ei werth y caiff pecyn ei orfodi: pan gaiff ei drosglwyddo i weithdrefn gyntefig a fydd yn defnyddio gwerth y pecyn; pan fydd yn werth traethiad mewn mynegiad amodol; a phan fydd yn werth gweithredydd sydd ar fin cael ei gymhwyso fel gweithdrefn. Un dewis dylunio sydd ar gael inni yw a ddylid cofio canlyniadau pecynnau gwerthuso gohiriedig, fel y gwnaethom â gwrthrychau gohiriedig yn 3.5.1. Wrth gofio canlyniadau, y tro cyntaf y gorfodir pecyn, mae'n storio'r gwerth a gyfrifir. Mae gorfodiadau dilynol yn dychwelyd y gwerth a storiwyd heb ailadrodd y cyfrifiad. Byddwn yn gwneud i'n dehonglydd gofio canlyniadau, gan fod hyn yn fwy effeithlon ar gyfer llawer o gymwysiadau. Ceir ystyriaethau dyrys yma, fodd bynnag.240
Addasu'r gwerthusydd
Y prif wahaniaeth rhwng y gwerthusydd diog a'r un yn 4.1 yw'r ffordd yr ymdrinnir â chymhwyso gweithdrefnau yn eval ac apply.
Mae cymal application? yn eval yn dod yn
((application? exp)
(apply (actual-value (operator exp) env)
(operands exp)
env))Mae hyn bron yr un fath â chymal application? yn eval yn 4.1.1. Ar gyfer gwerthuso diog, fodd bynnag, galwn apply gyda mynegiadau'r operandau, yn hytrach na'r argiau a gynhyrchir drwy eu gwerthuso. Gan y bydd angen yr amgylchedd arnom i adeiladu pecynnau gwerthuso gohiriedig os yw'r argiau i'w gohirio, rhaid inni drosglwyddo hwn hefyd. Rydym yn dal i werthuso'r gweithredydd, oherwydd mae angen y weithdrefn wirioneddol sydd i'w chymhwyso ar apply er mwyn dewis sut i weithredu yn ôl ei math (cyntefig neu gyfansawdd) a'i chymhwyso.
Pryd bynnag y mae angen gwerth gwirioneddol mynegiad arnom, defnyddiwn
(define (actual-value exp env)
(force-it (eval exp env)))yn hytrach nag eval yn unig, fel y caiff gwerthuso ei orfodi os yw gwerth y mynegiad yn becyn gwerthuso gohiriedig.
Mae ein fersiwn newydd o apply hefyd bron yr un fath â'r fersiwn yn 4.1.1. Y gwahaniaeth yw bod eval wedi trosglwyddo mynegiadau operand heb eu gwerthuso: Ar gyfer gweithdrefnau cyntefig (sy'n llym), gwerthuswn yr holl argiau cyn cymhwyso'r weithdrefn gyntefig; ar gyfer gweithdrefnau cyfansawdd (sy'n anllym), gohiriwn yr holl argiau cyn cymhwyso'r weithdrefn.
(define (apply procedure arguments env)
(cond ((primitive-procedure? procedure)
(apply-primitive-procedure
procedure
(list-of-arg-values
arguments
env))) ; changed
((compound-procedure? procedure)
(eval-sequence
(procedure-body procedure)
(extend-environment
(procedure-parameters procedure)
(list-of-delayed-args
arguments
env) ; changed
(procedure-environment procedure))))
(else (error "Unknown procedure
type: APPLY"
procedure))))Mae'r gweithdrefnau sy'n prosesu'r argiau yn union fel list-of-values o 4.1.1, ac eithrio bod list-of-delayed-args yn gohirio'r argiau yn lle eu gwerthuso, a bod list-of-arg-values yn defnyddio actual-value yn lle eval:
(define (list-of-arg-values exps env)
(if (no-operands? exps)
'()
(cons (actual-value
(first-operand exps)
env)
(list-of-arg-values
(rest-operands exps)
env))))
(define (list-of-delayed-args exps env)
(if (no-operands? exps)
'()
(cons (delay-it
(first-operand exps)
env)
(list-of-delayed-args
(rest-operands exps)
env))))Y man arall lle mae'n rhaid inni newid y gwerthusydd yw wrth ymdrin ag if, lle mae'n rhaid inni ddefnyddio actual-value yn lle eval i gael gwerth y mynegiad traethol cyn profi a yw'n wir neu'n anwir:
(define (eval-if exp env)
(if (true? (actual-value (if-predicate exp)
env))
(eval (if-consequent exp) env)
(eval (if-alternative exp) env)))Yn olaf, rhaid inni newid y weithdrefn driver-loop (4.1.4) i ddefnyddio actual-value yn lle eval, fel y caiff gwerth gohiriedig sy'n ymledu'n ôl i'r ddolen darllen–gwerthuso–argraffu ei orfodi cyn ei argraffu. Rydym hefyd yn newid yr anogiadau i ddangos mai hwn yw'r gwerthusydd diog:
(define input-prompt ";;; L-Eval input:")
(define output-prompt ";;; L-Eval value:")
(define (driver-loop)
(prompt-for-input input-prompt)
(let ((input (read)))
(let ((output (actual-value
input
the-global-environment)))
(announce-output output-prompt)
(user-print output)))
(driver-loop))Wedi gwneud y newidiadau hyn, gallwn gychwyn y gwerthusydd a'i brofi. Mae gwerthuso'r mynegiad try a drafodwyd yn 4.2.1 yn llwyddiannus yn dangos bod y dehonglydd yn cyflawni gwerthuso diog:
(define the-global-environment
(setup-environment))
(driver-loop)
;;; L-Eval input:
(define (try a b) (if (= a 0) 1 b))
;;; L-Eval value:
ok
;;; L-Eval input:
(try 0 (/ 1 0))
;;; L-Eval value:
1
Cynrychioli pecynnau gwerthuso gohiriedig
Rhaid i'n gwerthusydd drefnu creu pecynnau gwerthuso gohiriedig pan gymhwysir gweithdrefnau i argiau, a gorfodi'r pecynnau hyn yn ddiweddarach. Rhaid i becyn lapio mynegiad ynghyd â'r amgylchedd, fel y gellir cynhyrchu'r arg yn ddiweddarach. I orfodi'r pecyn, tynnwn y mynegiad a'r amgylchedd ohono a gwerthuswn y mynegiad yn yr amgylchedd. Defnyddiwn actual-value yn hytrach nag eval fel y byddwn, os yw gwerth y mynegiad ei hun yn becyn gwerthuso gohiriedig, yn gorfodi hwnnw hefyd, ac yn y blaen, nes cyrraedd rhywbeth nad yw'n becyn gwerthuso gohiriedig:
(define (force-it obj)
(if (thunk? obj)
(actual-value (thunk-exp obj)
(thunk-env obj))
obj))Un ffordd hawdd o becynnu mynegiad gydag amgylchedd yw gwneud rhestr sy'n cynnwys y mynegiad a'r amgylchedd. Felly, crëwn becyn gwerthuso gohiriedig fel a ganlyn:
(define (delay-it exp env)
(list 'thunk exp env))
(define (thunk? obj) (tagged-list? obj 'thunk))
(define (thunk-exp thunk) (cadr thunk))
(define (thunk-env thunk) (caddr thunk))Mewn gwirionedd, nid dyma'n union yr hyn a ddymunwn ar gyfer ein dehonglydd, ond yn hytrach becynnau gwerthuso gohiriedig sy'n cofio canlyniadau. Pan orfodir pecyn, byddwn yn ei droi'n becyn wedi'i werthuso drwy roi gwerth y mynegiad yn lle'r mynegiad a storiwyd, a newid y tag thunk fel y gellir cydnabod ei fod eisoes wedi'i werthuso.241
(define (evaluated-thunk? obj)
(tagged-list? obj 'evaluated-thunk))
(define (thunk-value evaluated-thunk)
(cadr evaluated-thunk))
(define (force-it obj)
(cond ((thunk? obj)
(let ((result
(actual-value
(thunk-exp obj)
(thunk-env obj))))
(set-car! obj 'evaluated-thunk)
;; replace exp with its value:
(set-car! (cdr obj) result)
;; forget unneeded env:
(set-cdr! (cdr obj) '())
result))
((evaluated-thunk? obj)
(thunk-value obj))
(else obj)))Sylwch fod yr un weithdrefn delay-it yn gweithio gyda chofio canlyniadau a hebddo.
Ymarfer 4.27: Tybiwch ein bod yn teipio'r diffiniadau canlynol i mewn i'r gwerthusydd diog:
(define count 0) (define (id x) (set! count (+ count 1)) x)Rhowch y gwerthoedd coll yn y dilyniant canlynol o ryngweithiadau, ac eglurwch eich atebion.242
(define w (id (id 10))) ;;; L-Eval input: count ;;; L-Eval value: ⟨response⟩ ;;; L-Eval input: w ;;; L-Eval value: ⟨response⟩ ;;; L-Eval input: count ;;; L-Eval value: ⟨response⟩
Ymarfer 4.28: Mae
Evalyn defnyddioactual-valueyn hytrach nagevali werthuso'r gweithredydd cyn ei drosglwyddo iapply, er mwyn gorfodi gwerth y gweithredydd. Rhowch enghraifft sy'n dangos yr angen am y gorfodi hwn.Ymarfer 4.29: Dangoswch raglen y byddech yn disgwyl iddi redeg yn llawer arafach heb gofio canlyniadau nag wrth gofio canlyniadau. Hefyd, ystyriwch y rhyngweithiad canlynol, lle diffinnir y weithdrefn
idfel yn Ymarfer 4.27 a lle maecountyn dechrau ar 0:(define (square x) (* x x)) ;;; L-Eval input: (square (id 10)) ;;; L-Eval value: ⟨response⟩ ;;; L-Eval input: count ;;; L-Eval value: ⟨response⟩Rhowch yr ymatebion pan fydd y gwerthusydd yn cofio canlyniadau a phan na fydd yn gwneud hynny.
Ymarfer 4.30: Mae Cy D. Fect, rhaglennydd C sydd wedi diwygio ei ffyrdd, yn poeni efallai na fydd rhai sgileffeithiau byth yn digwydd, oherwydd nad yw'r gwerthusydd diog yn gorfodi'r mynegiadau mewn dilyniant. Gan na ddefnyddir gwerth unrhyw fynegiad mewn dilyniant heblaw'r un olaf (mae'r mynegiad yno oherwydd ei effaith yn unig, megis aseinio i newidyn neu argraffu), ni all fod unrhyw ddefnydd dilynol o'r gwerth hwn (e.e., fel arg i weithdrefn gyntefig) a fydd yn achosi iddo gael ei orfodi. Felly mae Cy o'r farn bod rhaid inni, wrth werthuso dilyniannau, orfodi pob mynegiad yn y dilyniant ac eithrio'r un olaf. Mae'n cynnig addasu
eval-sequenceo 4.1.1 i ddefnyddioactual-valueyn hytrach nageval:(define (eval-sequence exps env) (cond ((last-exp? exps) (eval (first-exp exps) env)) (else (actual-value (first-exp exps) env) (eval-sequence (rest-exps exps) env))))
- Mae Ben Bitdiddle o'r farn bod Cy yn anghywir. Mae'n dangos i Cy y weithdrefn
for-eacha ddisgrifir yn Ymarfer 2.23, sy'n rhoi enghraifft bwysig o ddilyniant â sgileffeithiau:(define (for-each proc items) (if (null? items) 'done (begin (proc (car items)) (for-each proc (cdr items)))))Mae'n honni bod y gwerthusydd yn y testun (gyda'r
eval-sequencegwreiddiol) yn ymdrin â hyn yn gywir:;;; L-Eval input: (for-each (lambda (x) (newline) (display x)) (list 57 321 88)) 57 321 88 ;;; L-Eval value: doneEglurwch pam mae Ben yn gywir ynghylch ymddygiad
for-each.- Mae Cy yn cytuno bod Ben yn gywir ynghylch yr enghraifft
for-each, ond dywed nad dyna'r math o raglen oedd ganddo mewn golwg wrth gynnig ei newid ieval-sequence. Mae'n diffinio'r ddwy weithdrefn ganlynol yn y gwerthusydd diog:(define (p1 x) (set! x (cons x '(2))) x) (define (p2 x) (define (p e) e x) (p (set! x (cons x '(2)))))Beth yw gwerthoedd
(p1 1)ac(p2 1)gyda'reval-sequencegwreiddiol? Beth fyddai'r gwerthoedd gyda'r newid y mae Cy yn ei gynnig ieval-sequence?- Mae Cy hefyd yn nodi nad yw newid
eval-sequencefel y mae'n ei gynnig yn effeithio ar ymddygiad yr enghraifft yn rhan a. Eglurwch pam mae hyn yn wir.- Sut y credwch y dylid ymdrin â dilyniannau yn y gwerthusydd diog? A ydych yn hoffi dull Cy, y dull yn y testun, neu ryw ddull arall?
Ymarfer 4.31: Mae'r dull a ddefnyddir yn yr adran hon braidd yn annymunol, gan ei fod yn gwneud newid anghydnaws i Scheme. Efallai y byddai'n brafiach gweithredu gwerthuso diog fel estyniad sy'n gydnaws tuag i fyny, hynny yw, fel y bydd rhaglenni Scheme arferol yn gweithio fel o'r blaen. Gallwn wneud hyn drwy ymestyn cystrawen datganiadau gweithdrefnau i adael i'r defnyddiwr reoli a yw argiau i'w gohirio ai peidio. Tra byddwn wrthi, gallem hefyd roi'r dewis i'r defnyddiwr rhwng gohirio gyda chofio canlyniadau a hebddo. Er enghraifft, byddai'r diffiniad
(define (f a (b lazy) c (d lazy-memo)) …)yn diffinio
fyn weithdrefn â phedwar arg, lle gwerthusir yr arg cyntaf a'r trydydd pan elwir y weithdrefn, lle gohirir yr ail arg, a lle gohirir y pedwerydd arg a chofio ei ganlyniad. Felly, bydd diffiniadau gweithdrefn arferol yn cynhyrchu'r un ymddygiad â Scheme arferol, tra bydd ychwanegu'r datganiadlazy-memoat bob paramedr ym mhob gweithdrefn gyfansawdd yn cynhyrchu ymddygiad y gwerthusydd diog a ddiffinnir yn yr adran hon. Dyluniwch a gweithredwch y newidiadau angenrheidiol i gynhyrchu estyniad o'r fath i Scheme. Bydd angen ichi weithredu gweithdrefnau cystrawen newydd i ymdrin â'r gystrawen newydd ar gyferdefine. Rhaid ichi hefyd drefnu bodevalneuapplyyn penderfynu pryd mae argiau i'w gohirio, ac yn gorfodi neu'n gohirio argiau yn unol â hynny, a rhaid ichi drefnu bod gorfodi'n cofio canlyniadau neu beidio, fel y bo'n briodol.
4.2.3Ffrydiau fel Rhestrau Diog
Yn 3.5.1, dangoswyd sut i weithredu ffrydiau fel rhestrau gohiriedig. Cyflwynwyd y ffurfiau arbennig delay ac cons-stream, a oedd yn caniatáu inni adeiladu “addewid” i gyfrifo cdr ffrwd, heb gyflawni'r addewid hwnnw tan yn ddiweddarach. Gallem ddefnyddio'r dechneg gyffredinol hon o gyflwyno ffurfiau arbennig pryd bynnag y mae angen mwy o reolaeth arnom dros y broses werthuso, ond mae hyn yn lletchwith. Yn un peth, nid yw ffurf arbennig yn wrthrych dosbarth cyntaf fel gweithdrefn, felly ni allwn ei defnyddio ynghyd â gweithdrefnau trefn uwch.243 Yn ogystal, bu'n rhaid inni greu ffrydiau fel math newydd o wrthrych data tebyg i restrau ond nid yn union yr un fath, ac roedd hyn yn mynnu ein bod yn ailweithredu llawer o weithrediadau rhestr arferol (map, append, ac yn y blaen) i'w defnyddio gyda ffrydiau.
Gyda gwerthuso diog, gall ffrydiau a rhestrau fod yn union yr un fath, felly nid oes angen ffurfiau arbennig na gweithrediadau ar wahân ar gyfer rhestrau a ffrydiau. Y cyfan sydd angen inni ei wneud yw trefnu bod cons yn anllym. Un ffordd o gyflawni hyn yw ymestyn y gwerthusydd diog i ganiatáu gweithdrefnau cyntefig anllym, a gweithredu cons fel un o'r rhain. Ffordd haws yw cofio (2.1.3) nad oes angen sylfaenol gweithredu cons fel gweithdrefn gyntefig o gwbl. Yn hytrach, gallwn gynrychioli parau fel gweithdrefnau:244
(define (cons x y) (lambda (m) (m x y)))
(define (car z) (z (lambda (p q) p)))
(define (cdr z) (z (lambda (p q) q)))Yn nhermau'r gweithrediadau sylfaenol hyn, bydd diffiniadau safonol y gweithrediadau rhestr yn gweithio gyda rhestrau anfeidraidd (ffrydiau) yn ogystal â rhai meidraidd, a gellir gweithredu'r gweithrediadau ffrwd fel gweithrediadau rhestr. Dyma rai enghreifftiau:
(define (list-ref items n)
(if (= n 0)
(car items)
(list-ref (cdr items) (- n 1))))
(define (map proc items)
(if (null? items)
'()
(cons (proc (car items))
(map proc (cdr items)))))
(define (scale-list items factor)
(map (lambda (x) (* x factor))
items))
(define (add-lists list1 list2)
(cond ((null? list1) list2)
((null? list2) list1)
(else (cons (+ (car list1)
(car list2))
(add-lists
(cdr list1)
(cdr list2))))))
(define ones (cons 1 ones))
(define integers
(cons 1 (add-lists ones integers)))
;;; L-Eval input:
(list-ref integers 17)
;;; L-Eval value:
18
Sylwch fod y rhestrau diog hyn hyd yn oed yn ddiogach na ffrydiau Pennod 3: Gohirir car y rhestr, yn ogystal â'i cdr.245 Yn wir, nid oes angen i hyd yn oed gyrchu car neu cdr pâr diog orfodi gwerth elfen rhestr. Dim ond pan fydd ei wir angen y gorfodir y gwerth—e.e., i'w ddefnyddio fel arg gweithdrefn gyntefig, neu i'w argraffu fel ateb.
Mae parau diog hefyd yn helpu gyda'r broblem a gododd gyda ffrydiau yn 3.5.4, lle gwelsom y gall llunio modelau ffrwd o systemau â dolenni fynnu ein bod yn gwasgaru gweithrediadau delay penodol drwy ein rhaglenni, y tu hwnt i'r rhai a ddarperir gan cons-stream. Gyda gwerthuso diog, gohirir holl argiau gweithdrefnau'n unffurf. Er enghraifft, gallwn weithredu gweithdrefnau i integru rhestrau a datrys hafaliadau differol fel y bwriadwyd yn wreiddiol yn 3.5.4:
(define (integral integrand initial-value dt)
(define int
(cons initial-value
(add-lists (scale-list integrand dt)
int)))
int)
(define (solve f y0 dt)
(define y (integral dy y0 dt))
(define dy (map f y))
y)
;;; L-Eval input:
(list-ref (solve (lambda (x) x) 1 0.001) 1000)
;;; L-Eval value:
2.716924
Ymarfer 4.32: Rhowch rai enghreifftiau sy'n dangos y gwahaniaeth rhwng ffrydiau Pennod 3 a'r rhestrau diog “diogach” a ddisgrifir yn yr adran hon. Sut gallwch fanteisio ar y diogi ychwanegol hwn?
Ymarfer 4.33: Mae Ben Bitdiddle yn profi'r gweithrediad rhestrau diog a roddir uchod drwy werthuso'r mynegiad
(car '(a b c))Er ei syndod, mae hyn yn cynhyrchu gwall. Ar ôl meddwl ychydig, mae'n sylweddoli bod y “rhestrau” a geir drwy ddarllen mynegiadau wedi'u dyfynnu yn wahanol i'r rhestrau a drinnir gan y diffiniadau newydd o
cons,car, accdr. Addaswch y ffordd y mae'r gwerthusydd yn ymdrin â mynegiadau wedi'u dyfynnu fel bod rhestrau wedi'u dyfynnu a deipir wrth y ddolen yrru yn cynhyrchu rhestrau diog go iawn.
Ymarfer 4.34: Addaswch ddolen yrru'r gwerthusydd fel y caiff parau a rhestrau diog eu hargraffu mewn rhyw ffordd resymol. (Beth ydych yn mynd i'w wneud am restrau anfeidraidd?) Efallai y bydd angen ichi hefyd addasu cynrychiolaeth parau diog fel y gall y gwerthusydd eu hadnabod er mwyn eu hargraffu.
Troednodiadau
235 Snarf: “Cipio, yn enwedig dogfen neu ffeil fawr er mwyn ei defnyddio naill ai gyda chaniatâd y perchennog neu hebddo.” Snarf down: “Cipio felly, weithiau gyda'r ystyr ychwanegol o amsugno, prosesu, neu ddeall.” (Cipiwyd y diffiniadau hyn o Steele et al. 1983. Gweler hefyd Raymond 1993.)
236 Mae'r gwahaniaeth rhwng terminoleg “diog” a therminoleg “trefn arferol” braidd yn annelwig. Yn gyffredinol, mae “diog” yn cyfeirio at fecanweithiau gwerthusyddion penodol, tra bo “trefn arferol” yn cyfeirio at semanteg ieithoedd, yn annibynnol ar unrhyw strategaeth werthuso benodol. Ond nid yw hyn yn wahaniaeth pendant, a defnyddir y ddwy derminoleg yn aml yn gyfnewidiol.
237 Mae'r derminoleg “llym” yn erbyn “anllym” yn golygu'r un peth yn ei hanfod â “trefn gymhwysol” yn erbyn “trefn arferol,” ac eithrio ei bod yn cyfeirio at weithdrefnau ac argiau unigol yn hytrach na'r iaith gyfan. Mewn cynhadledd ar ieithoedd rhaglennu efallai y clywech rywun yn dweud, “Mae gan yr iaith trefn arferol Hassle rai gweithdrefnau cyntefig llym. Mae gweithdrefnau eraill yn derbyn eu hargiau drwy werthuso diog.”
238 Dyfeisiwyd y gair thunk gan weithgor anffurfiol a oedd yn trafod gweithredu galw yn ôl enw yn Algol 60. Sylwasant y gellid gwneud y rhan fwyaf o ddadansoddi'r mynegiad (“meddwl amdano”) adeg crynhoi; felly, adeg rhedeg, byddai'r mynegiad eisoes wedi cael ei feddwl amdano—“thunk,” ffurf chwareus ar “thought” (Ingerman et al. 1960).
239 Mae hyn yn cyfateb i ddefnyddio force ar y gwrthrychau gohiriedig a gyflwynwyd ym Mhennod 3 i gynrychioli ffrydiau. Y gwahaniaeth hanfodol rhwng yr hyn a wnawn yma a'r hyn a wnaethom ym Mhennod 3 yw ein bod yn adeiladu gohirio a gorfodi i mewn i'r gwerthusydd, ac felly'n gwneud hyn yn unffurf ac yn awtomatig drwy'r iaith gyfan.
240 Weithiau gelwir gwerthuso diog ynghyd â chofio canlyniadau yn drosglwyddo argiau drwy alw yn ôl angen, mewn cyferbyniad â throsglwyddo argiau drwy alw yn ôl enw. (Mae galw yn ôl enw, a gyflwynwyd yn Algol 60, yn debyg i werthuso diog heb gofio canlyniadau.) Fel dylunwyr ieithoedd, gallwn adeiladu ein gwerthusydd i gofio canlyniadau, i beidio â'u cofio, neu i adael hyn yn ddewis i raglenwyr (Ymarfer 4.31). Fel y gallech ddisgwyl o Bennod 3, mae'r dewisiadau hyn yn codi materion sy'n dod yn gynnil ac yn ddryslyd ym mhresenoldeb aseiniadau. (Gweler Ymarfer 4.27 ac Ymarfer 4.29.) Mae erthygl ragorol gan Clinger (1982) yn ceisio egluro'r dryswch amlweddog sy'n codi yma.
241 Sylwch ein bod hefyd yn dileu'r env o'r pecyn gwerthuso gohiriedig ar ôl cyfrifo gwerth y mynegiad. Nid yw hyn yn newid y gwerthoedd a ddychwelir gan y dehonglydd. Mae'n helpu i arbed lle, fodd bynnag, oherwydd mae dileu'r cyfeiriad o'r pecyn at yr env pan nad oes ei angen mwyach yn caniatáu i'r strwythur hwn gael ei gasglu fel sbwriel ac i'w le gael ei ailddefnyddio, fel y byddwn yn ei drafod yn 5.3.
Yn yr un modd, gallem fod wedi caniatáu casglu amgylcheddau diangen y gwrthrychau gohiriedig sy'n cofio canlyniadau yn 3.5.1 fel sbwriel, drwy wneud i memo-proc wneud rhywbeth fel (set! proc '()) i waredu'r weithdrefn proc (sy'n cynnwys yr amgylchedd lle gwerthuswyd y delay) ar ôl storio ei gwerth.
242 Mae'r ymarfer hwn yn dangos y gall y rhyngweithio rhwng gwerthuso diog a sgileffeithiau fod yn ddryslyd iawn. Dyma'n union yr hyn y gallech ei ddisgwyl o'r drafodaeth ym Mhennod 3.
243 Dyma'n union y broblem gyda'r weithdrefn unless, fel yn Ymarfer 4.26.
244 Dyma'r gynrychiolaeth weithdrefnol a ddisgrifir yn Ymarfer 2.4. Byddai bron unrhyw gynrychiolaeth weithdrefnol (e.e., gweithrediad trosglwyddo negeseuon) yn gwneud cystal. Sylwch y gallwn osod y diffiniadau hyn yn y gwerthusydd diog drwy eu teipio wrth y ddolen yrru. Os oeddem wedi cynnwys cons, car, ac cdr yn wreiddiol fel gweithdrefnau cyntefig yn yr amgylchedd byd-eang, cânt eu hailddiffinio. (Gweler hefyd Ymarfer 4.33 ac Ymarfer 4.34.)
245 Mae hyn yn caniatáu inni greu fersiynau gohiriedig o fathau mwy cyffredinol o strwythurau rhestr, nid dilyniannau yn unig. Mae Hughes 1990 yn trafod rhai cymwysiadau o “goed diog.”