日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

SICP第三章题解

發(fā)布時間:2023/12/19 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 SICP第三章题解 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

目錄

  • SICP第三章題解
    • ex3-17
    • ex3-18
    • ex3-19
    • 隊列
    • ex3-21
    • ex3-22
    • ex3-24
    • ex3-25
    • 3.4 并發(fā):時間是一個本質(zhì)問題
    • ex3-38
    • 3.4.2 控制并發(fā)的機制
    • ex3-39
    • ex3-41
    • ex3-42
    • 串行化、序列化
    • ex3-44
    • 串行化的實現(xiàn)
    • ex3-47
    • 死鎖
    • 3.5 流
    • ex3-50
    • 序列加速器

SICP第三章題解

標(biāo)簽(空格分隔): SICP


ex3-17

統(tǒng)計一個表結(jié)構(gòu)中的序?qū)€數(shù)

(define (count-pairs x)(count-helper x '()))(define (count-helper x seq)(if (memq? x seq)(count-helper (cdr x) seq)(count-helper (cdr x) (list x seq))) )

ex3-18

判斷一個表中是否包含環(huán)。
我的思路:還是用memq去判斷。

(define (judge-cycle x)(judge-cycle-helper x '()))(define (judge-cycle-helper x seq)(cond ((null? x) #f)((memq? (car x) seq) #t)(else(judge-cycle-helper (cdr x) (list x seq)))) )

ex3-19

重做ex3-18,采用一種只需要常量空間的做法。
我的思路:難道是用套圈的方式嗎?相當(dāng)于兩個人跑步,一個人速度為v,另一個速度為2v,如果某人遇到nil就結(jié)束,如果兩人除了開始節(jié)點外還有同樣的節(jié)點就是套圈了。

(define (judge-cycle x)(cond ((null? (cdr x)) #f)((null? (cddr x)) #f)(judge-cycle-helper (cdr x) (cddr x))) )(define (judge-cycle-helper x y)(cond ((null? (cdr x)) #f)((null? (cddr y)) #f)((eq? (car x) (car y)) #t)(elsejudge-cycle-helper (cdr x) (cddr y))) )

隊列

設(shè)定一個queue,是cons序?qū)?#xff0c;用來在O(1)時間內(nèi)訪問front和rear。這方法確實不錯。

;;構(gòu)造隊列,默認(rèn)為空隊列 (define (make-queue) (cons '() '()));;隊首指針 (define (front-ptr queue) (car queue)) ;;隊尾指針 (define (rear-ptr queue) (cdr queue));;隊列判空。這里發(fā)現(xiàn)中文譯文不是很準(zhǔn)確。英文原文: ;;We will consider a queue to be empty if its front pointer is the empty list (define (empty-queue? queue) (null? (front-ptr queue)));;隊首元素 (define (front-queue queue)(if (empty-queue? queue)(error "FRONT called with an empty queue" queue)(car (front-ptr queue))) )

ex3-21

原因在于,把queue中用來指引頭指針和為指針的q的cdr也打印了。不打印cdr,只打印car即可。

(define (print-queue q)(car q) )

ex3-22

將隊列構(gòu)造成一個帶有局部狀態(tài)的過程

(define (make-queue)(let ( (front-ptr '())(rear-ptr '()))(define (dispatch m)(cond ((eq? m 'insert-queue!) inserrt-queue!)((eq? m 'delete-queue!) delete-queue!)((eq? m 'empty-queue?) empty-queue?)(else(error "Unknown operation -- DISPATCH" m))))dispatch) )

ex3-24

我認(rèn)為本題重寫assoc過程即可。

(define (make-table same-key?)(let ((local-table (list '*table*)))(define (lookup key-1 key2)(let ((subtable (assoc key-1 (cdr local-table))))(if subtable(let ((record (assoc key-2 (cdr subtable))))(if record(cdr record)#f))#f)))(define (assoc key records)(cond ((null? records) false)((same-key? key (caar records)) (car records))(else (assoc key (cdr records)))))(define (dispatch m)(cond ((eq? m 'lookup-proc) lookup)((eq? m 'insert-proc!) insert!)(else (error "Unknown operation -- TABLE" m))))dispatch) )

ex3-25

用遞歸做。

(define (lookup key-list table)(if (list? key-list)(let ((record (assoc ((cdr key-list) (cdr table)))))(if record(if (null? (cdr key-list))(cdr record)(lookup (cdr key-list) record))#f));;else#f) )(define (insert! key-list value table)(if (list? key-list)(let ((record (assoc (car key-list) (cdr table))))(if record(if (null? (cdr key-list))(set-cdr! record value)(insert! (cdr key-list) value (cdr table)))(set-cdr! table(cons (list (key-list value)) (cdr table)))));;else#f) )

3.4 并發(fā):時間是一個本質(zhì)問題

為什么Erlang適合高并發(fā)?我猜測是,Erlang中局部變量非常少,基本上沒有內(nèi)部變量,因此不會涉及太多訪問順序的問題。

對一個表達式求值的結(jié)果不僅依賴于該表達式本身,還依賴于求值發(fā)生在這些時刻之前還是之后:時間(順序)是一個本質(zhì)問題。

比如兩個人都能操作同一個銀行賬戶,同時取錢就可能產(chǎn)生錯誤:只要取錢過程不是原子操作(比如沒有被鎖住),就可能使內(nèi)部變量的值算錯。但是,怎樣實現(xiàn)原子操作

P.S.終于看到書的一半了!(經(jīng)典的書值得慢慢讀)

ex3-38

mary->peter->paul 40
mary->paul->peter 40
peter->mary->paul 35
peter->paul->mary 45
paul->mary->peter 50
paul->peter->mary 45

3.4.2 控制并發(fā)的機制

串行訪問:進程能并發(fā)執(zhí)行,但過程不能并發(fā)執(zhí)行。
具體說就是:串行化操作會創(chuàng)建若干“過程集”,每個“過程集”中的過程只能串行執(zhí)行,如果一個進程要執(zhí)行一個“過程集”的一個過程,就要等到這個“過程集”當(dāng)前執(zhí)行的過程執(zhí)行完畢才可以執(zhí)行。

用串行化能控制共享變量的訪問。比如,修改變量S的操作依賴于S原有的值,那么我們把讀取S和給S賦值的操作放到同一個過程。并且還要設(shè)法保證,其他任何給S賦值的過程,能和這個過程并發(fā)執(zhí)行;具體做法是:把其他為S賦值的操作與這個操作(讀取S再修改S這個過程)放到一個串行化集合(即“過程集”)里面。

ex3-39

一個思路是把(set!)表達式抽象出來看作一個整體。因為 ((s (lambda () (* x x)))) 和 ((s (lambda () (set! x (+ x 1))))) 都是串行化操作,因此可以將它們看作是一個單獨的執(zhí)行單位 sa 和 sb ,并將題目給出的表達式轉(zhuǎn)換成以下表示:

(parallel-execute (lambda () (set! x sa))sb)

以上表達式可能的執(zhí)行序列有以下這些( ? 符號表示執(zhí)行過程被其他操作打斷):

sb –> (set! x sa) (set! x ?) –> sb –> (set! x sa) (set! x sa) –> sb

這些執(zhí)行序列會產(chǎn)生以下結(jié)果:

(set! x (+ 10 1)) => x = 11 => (set! x (* 11 11)) => x = 121 [計算 sa=100] => (set! x (+ 10 1) => x = 11 => (set! x sa) => x = 100 (set! x (* 10 10)) => x = 100 => (set! x (+ 100 1)) => x = 101

ex3-41

Ben的做法沒有必要。讀取balance變量的值,這一操作本身就是原子的。

ex3-42

和上面一題應(yīng)該是一致的效果,是安全的。不同點在于,ex-3.41是調(diào)用deposit或withdraw時產(chǎn)生響應(yīng)的串行過程,而ex-3.42是在調(diào)用make-account的時候返回的過程中就包含了withdraw和deposit對應(yīng)的串行過程。

雖然ex-3.42使用的是same serializer(同一個串行化過程),但是因為串行化過程本身就是一個原子操作,同一個make-account生成的對象的并發(fā)調(diào)用withdraw或deposit的操作,還是會被正確執(zhí)行。

串行化、序列化

java里有關(guān)鍵字Serializable,意思是(對象)序列化。
稍微搜了下java Serializable,排名靠前的文章都沒有提到并發(fā)問題。think in java中似乎也沒有提到serializable和并發(fā)是相關(guān)的。

但讀SICP的P214時候,明顯感覺到,串行化(序列化)就是使進程可以并發(fā)執(zhí)行的一種解決辦法。大家都沒有注意到嗎?

ex3-44

Louis多慮了,并不需要更復(fù)雜精細的方法。交換操作要求交換的雙方都處于空閑狀態(tài)。

串行化的實現(xiàn)

終于到討論Serializable的實現(xiàn)的時候了:用mutex實現(xiàn)。

mutex是mutual exclusion的縮寫:互斥量,是信號量機制的一種簡化形式。信號量來自THE操作系統(tǒng),由Dijkstra提出,主要是經(jīng)典的PV操作。

在我們的實現(xiàn)里,每個串行化組關(guān)聯(lián)著一個互斥元;給了一個過程P,串行化組將返回一個過程,該過程將獲取相應(yīng)互斥元,而后運行P,而后釋放該互斥元。這樣就保證,由這個串行化組產(chǎn)生的所有過程中,一次只能運行一個,這就是需要保證的串行化性質(zhì)。

P.S. P219提到:在當(dāng)前的多處理器系統(tǒng)里,串行化方式正在被并發(fā)制的各種新技術(shù)取代

(define (make-serializer)(let ((mutex (make-mutex)))(lambda (p)(define (serialized-p . args)(mutex 'acquire)(let ((val (apply p args)))(mutex 'release)val))serialized-p)) )

看到LISP的代碼被我寫成這個樣子,我才發(fā)現(xiàn),Python用縮進(indent)是多么正確的一件事:各種反括號都不用寫了!

互斥元的實現(xiàn)

(define (make-mutex)(let ((cell (list false)))(define (the-mutex m)(cond ((eq? m 'acquire);;注意:if語句不寫else分支也是ok的(if (test-and-set! cell)(the-mutex 'acquire)))((eq? m 'release) (clear! cell))))the-mutex) )(define (clear! cell)(set-car! cell false) )(define (test-and-set! cell)(if (car cell)true(begin (set-car! cell true)false)) )

這里的一個細節(jié)是:需要保證test-and-set!過程的原子性:顯然,一旦cell的值為false,那么測試cell的值和修改cell的值這兩個過程就要一氣呵成。

對于單處理器,如果是分時系統(tǒng),只要保證在檢查和設(shè)置cell值之間禁止進行時間分片,就能保證原子性。
對于多處理器,硬件中已經(jīng)支持原子操作了。

ex3-47

實現(xiàn)信號量。

  • 基于互斥元的實現(xiàn)
  • (define (make-semaphore n)(let ((mutex (make-mutex)))(define (acquire)(mutex 'acquire)(if (> n 0) (begin (set! n (- n 1))(mutex 'release))(begin(mutex 'release)(acquire))))(define (release)(mutex 'acquire)(set! n (+ n 1) )(mutex 'release))(define (dispatch m)(cond ((eq? m 'acquire) (acquire))((eq? m 'release) (release))(else (error "Unknown mode MAKE-SEMAPHORE" mode))))dispatch) )
  • 基于原子的test-and-set!操作
  • (define (make-semaphore n)(let ((cell (list #f))) ;;modified(define (request m)(cond ((eq? m 'acquire)(if (test-and-set! cell)(request m)(cond ((= n 0)(clear! cell)(request m))(else(begin (set! n (- n 1))(clear! cell))))))((eq? m 'release)(if (test-and-set! cell)(request m)(begin(set! n (+ n 1))(clear! cell))))(else (error "Unknown request" m))))request) )

    但是其實這里內(nèi)部變量cell仍然是一個mutex(信號量)。。

    死鎖

    有了前面“過程集”的概念作為鋪墊,這里理解死鎖就很容易了:比如當(dāng)前并發(fā)進程P1和P2涉及到兩個過程集S1和S2,每個進程都需要兩個過程集里面的操作,但是由于一個過程集里同一時刻只能有一個過程被執(zhí)行,一旦兩個進程分別執(zhí)行S1,S2中的過程,并且還要求執(zhí)行另一個進程集里的過程,就產(chǎn)生了死鎖。

    小結(jié)一下:

    并發(fā)問題==>用“串行化”解決==》但會產(chǎn)生“死鎖”||||\/用mutex(互斥量)實現(xiàn)

    3.5 流

    用delay和force實現(xiàn)延遲和強制求值,能實現(xiàn)流操作。
    最簡單的實現(xiàn):

    (define (delay exp)(lambda () exp) )(define (force delayed-object)(delayed-object) )

    帶記憶功能的實現(xiàn):

    (define (memo-proc)(let ((already-run? false) (result false))(if (not already-run?)(begin (set! result (proc))(set! already-run? true)result))) )(define (dalay exp)(memo-proc (lambda () exp)) )

    ex3-50

    實現(xiàn)推廣的stream-map

    (define (stream-map proc . argstreams)(if (null? (car argstreams))'()(cons-stream(apply proc (map (lambda (s) (stream-car s))argstreams))(apply stream-map(cons proc (map (lambda (s) (stream-cdr s))argstreams))))) )

    Henderson圖,遞歸地表示了信號處理流程。

    序列加速器

    歐拉提出的方法,對于交錯級數(shù)的部分和十分有效。比如S(n)表示前n項和,那么S(n+1)-(S(n+1)-S(n))^2/(S(n-1)-2S(n)+S(n+1))就是加速序列

    用這種方法逼近π,只需要8次計算,就能算到14位精度,而如果不使用加速,那么需要10^13數(shù)量級的項才能算到同樣的精度。

    歐拉真猛!

    總結(jié)

    以上是生活随笔為你收集整理的SICP第三章题解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。