居眠り床屋を書いてみた

d:id:sumim:20090923
居眠り床屋問題どう書く?org


結城さんの本に出てるProducerConsumerみたいな?
がんばればいけそうな気がしたので書いてみた。

| logger random barber q count customers |
Transcript clear.

q := SharedQueue new.
random := Random new.
count := 0.

logger := [ :msg |
  | h len |
  h := Processor activeProcess identityHasn.
  len := h printString size.
  Transcript
    show: '[';
    show: (String new: 5 - len withAll: $ );
    show: h printString;
    show: '] ';
    show: msg;
    cr.
].

Transcript show: 'start'; cr.

barber := [
  [
    q size = 0
      ifTrue: [
        logger value: ': sleep.'.
        Processor activeProcess suspend.
        logger value: ': awake.'.
      ]
      ifFalse: [
        | c |
        c := q peek.
        logger value: ': cut ', c printString, ' start'.

        (Delay forMilliseconds: 
          ((random next * 300) + 100) truncated
        ) wait.

        q next.
        logger value: ': cut ', c printString, ' done.'.
        count := count + 1
      ].
  ] repeat
] fork.

customers := OrderedCollection new.
(1 to: 16) do: [ :seq |
  | customer |
  (Delay forMilliseconds: (
    seq = 9
      ifTrue: [ 1200 ]
      ifFalse: [ (random next * 200) truncated ]
  )) wait.

  customer := [
    logger value: seq printString, ': in.'.
    barber processState = #suspend
      ifTrue: [ barber resume ].

    q size >= 3
      ifTrue: [ logger value: seq printString, ': out.' ]
      ifFalse: [ q nextPut: seq ]
  ].
  customers add: customer fork.
].

[ (customers reject: [ :p | p processState = #terminated ]) notEmpty ] whileTrue: [
  (Delay forSeconds: 1) wait.
].
barber terminate.

Transcript show: '* ', count printString, ' / 16 cut.'; cr.
Transcript show: 'end'; cr.

"
start
[15649] : sleep.
[ 3131] 1: in.
[15649] : awake.
[15649] : cut 1 start
[13256] 2: in.
[ 6997] 3: in.
[15649] : cut 1 done.
[15649] : cut 2 start
[  738] 4: in.
[10863] 5: in.
[10863] 5: out.
[ 4604] 6: in.
[ 4604] 6: out.
[15649] : cut 2 done.
[15649] : cut 3 start
[14729] 7: in.
[15649] : cut 3 done.
[15649] : cut 4 start
[ 8470] 8: in.
[15649] : cut 4 done.
[15649] : cut 7 start
[15649] : cut 7 done.
[15649] : cut 8 start
[15649] : cut 8 done.
[15649] : sleep.
[ 2211] 9: in.
[15649] : awake.
[15649] : cut 9 start
[15649] : cut 9 done.
[15649] : sleep.
[12336] 10: in.
[15649] : awake.
[15649] : cut 10 start
[ 6077] 11: in.
[16202] 12: in.
[15649] : cut 10 done.
[15649] : cut 11 start
[ 9943] 13: in.
[15649] : cut 11 done.
[15649] : cut 12 start
[ 3684] 14: in.
[13809] 15: in.
[13809] 15: out.
[ 1291] 16: in.
[ 1291] 16: out.
[15649] : cut 12 done.
[15649] : cut 13 start
[15649] : cut 13 done.
[15649] : cut 14 start
[15649] : cut 14 done.
[15649] : sleep.
* 12 / 16 cut.
end
"

ブラウザとにらめっこしながらなんとかできた。
つか、結果合ってるんだろうか。たまたまだけど寝まくってる。
のわりには、インスパイアかと思うくらい模範解答とかぶりまくり。


結果文字列をTranscriptからコピペしようと思ったら途中で切れちゃってちょっと悩んだけど、String class>>new:でできる文字列が\0でフィルされているせいかな。String class>>new:withAll:で$ で埋めるようにしたらちゃんとできたんで、きっとそういうことなんだろう。


Process/Promise/Semaphoreがなにがなんだかわかってないので、あとでちゃんと復習する。
Smalltalkイディオムとかも読む。