The King's Museum

ソフトウェアエンジニアのブログ。

『プロフェッショナルの条件』を読んで

今年二冊目の本。ドラッカーの『プロフェッショナルの条件』。

ドラッカーが組織や社会ではなく個人に焦点を置いて書いた本。

より正確には「書いた本」ではなく「まとめた本」かな。

ドラッカーの過去の著作や論文から抜粋・加筆・修正してまとめあげたのがこの本。 そのせいか、全体的にまとまりがなかったように思う。 話があっちにいったりこっちにいったりする。

個人に焦点に書かれた本だから仕事術とか自己啓発的な部分があるわけだけど、ちょっと抽象的だし説明がまわりくどい。 今はもっと実践的でシンプルに説明された本がたくさんあるので、そちらを読む方が身につくことは多いかも。 きっと、それらの本の元ネタがドラッカーだったりするのだろうけど。

個人の寿命が所属する組織の寿命よりも長くなった、というのは読んでみて改めて再認識したところかな。 自分は今の会社で定年を迎えるなんてぜんぜん考えてないし、そういう時代は終わったと思っていたけど、ドラッカー先生が言っているとやはり説得力がある。

ポスト資本主義とか新しい社会がどうなるか、という話は正直よく分からなかった。 「知識労働者が〜」とか「組織社会が〜」とか。 個々の話はなんとなく「なるほどね〜」って気持ちになったけど、結局全体として何が言いたかったのかは「よくわからんわ・・・」って感じ。

人生で初めてドラッカーを読んだけど、正直なところ得られたものは少なかった。 どうせ読むならこういう抜粋形式の本ではなく、ちゃんとしたドラッカーの本を一つ読んだ方がよかったかもしれない。

題名に「はじめて読むドラッカー」って書かれてるけど、はじめて読む人にはあまり向いてないと思う。 すでにドラッカーを一通り読んでいて「ふむふむ、やっぱりそういうことだよね」って再認識するための本なんだと思う。

Amazon Aurora について

業務で Amazon Aurora を使っているが、ほとんど何も意識せず MySQL の代替として使ってる。

何が違うのか。 せっかく使っているのだから、少し勉強したほうがよさそう。 ということで、Aurora についてまとめられた資料を読んだ。

www.slideshare.net

ストレージがマスターとスレーブ(レプリカ)で共通のものを使っているところがアーキテクチャとしては特徴的。 このおかげでレプリケーションが速かったり、いろいろとメリットがあるようだ。

また、書き込みエンドポイント・読み取りエンドポイントが存在し、読み取りエンドポイントは各レプリカインスタンスに負荷分散する。

あとは、教科書通りのメリット(パフォーマンスや運用上のメリット)がずらりと。

余談

スライドにはデータベースの実装としての特徴がいくつも書いてあったけど、正直よく分からず。完全な背景知識不足。

こういう時に、分からない部分をどこまで追うかはいつも迷う。

技術者としては、こういう機会を生かして知識を蓄える必要があると思うのだが、適度に割り切って目の前の仕事を進める必要もある。 そのバランスをどうするかはいつも迷うが、最近は知識の蓄えの方をサボりがちかもしれない。

Scheme 手習い(6)

第7章:友達と親類

set?

(define (set? lat)
  (cond [(null? lat) #t]
        [(member? (car lat) (cdr lat)) #f]
        [else (set? (cdr lat))]))

lat が重複要素を持たない(= セットかどうか)かを調べる関数。 前章までですでに定義した手続き member? を利用する。

makeset

(define (makeset lat)
  (cond [(null? lat) '()]
        [(member? (car lat) (cdr lat)) (makeset (cdr lat))]
        [else (cons (car lat) (makeset (cdr lat)))]))

(define (makeset lat)
  (cond [(null? lat) '()]
        [else (cons (car lat)
                    (makeset (multirember (car lat) (cdr lat))))]))

makeset の2つのバリエーション。 member? を使うか multirember を使うかでリスト内の要素の順序が変わる。 機能は同じだけど実装が異なるという話。

subset?

(define (subset? set1 set2)
  (cond [(null? set1) #t]
        [else (and (member? (car set1) set2)
                   (subset? (cdr set1) set2))]))

set1 が set2 の部分集合かどうかを調べる関数。 and をうまく使うと短く書ける。

eqset?

(define (eqset? set1 set2)
  (and (subset? set1 set2) (subset? set2 set1)))

set1 と set2 が等しい集合かどうかを調べる関数。 subset? を使うとうまく簡潔に書ける。

intersect?

(define (intersect? set1 set2)
  (cond [(null? set1) #f]
        [else (or (member? (car set1) set2)
                  (intersect? (cdr set1) set2))]))

二つの集合に共通部分があるかどうかを調べる関数。 set1 の要素が1つでも set2 にあったら true となる。

intersect

(define (intersect set1 set2)
  (cond [(null? set1) '()]
        [(member? (car set1) set2)
         (cons (car set1) (intersect (cdr set1) set2))]
        [else (intersect (cdr set1) set2)]))

二つの集合の積集合を得る関数。

union

(define (union set1 set2)
  (cond [(null? set1) set2]
        [(member? (car set1) set2)
         (union (cdr set1) set2)]
        [else (cons (car set1) (union (cdr set1) set2))]))

二つの集合の和集合を得る関数。 実装としてはちょうど intersect の反対のような処理を行う。

intersectall

(define (intersectall l-set)
  (cond [(null? (cdr l-set)) (car l-set)]
        [(intersect (car l-set) (intersectall (cdr l-set)))]))

集合のリストからすべての集合の積集合を得るための関数。 リスト内のそれぞれの集合に intersect を再帰的に適用する。 もし、集合 A、B、C、D があったら、

(intersect A (intersect B (intersect C (intersect D))))

と適用していくイメージ。((intersect D) = D である)

a-pair?

(define (a-pair? x)
  (cond [(atom? x) #f]
        [(null? x) #f]
        [(null? (cdr x)) #f]
        [(null? (cdr (cdr x))) #t]
        [else #f]))

対象がペアかどうかを調べる関数。 ペアとは Lisp における対ではなくて、要素がふたつのリストという意味。

fun?

(define (fun? rel)
  (cond [(null? rel) #t]
        [else (set? (firsts rel))]))

対象の関係(rel)が関数であるかどうかを調べる。 関数であるためには、入力(第一要素)が重複してないことが条件。

revrel

(define (revrel rel)
  (cond [(null? rel) '()]
        [else (cons
               (build (second (car rel))
                      (first (car rel)))
               (revrel (cdr rel)))]))

対象の関係(rel)を逆にする関数。 第一要素と第二要素を入れ替えて、リストを構築し直す。

fulfun?

(define (fullfun? rel)
  (cond [(null? rel) #t]
        [else (set? (seconds rel))]))

第二要素に重複がないかどうかを調べる関数。

one-to-one?

(define (one-to-one? rel)
  (fun? (revrel fun)))

full-fun? を fun? と revrel を使って書き直した関数。 one-to-one は単射の意味(だと思う)。

感想

集合の操作がメイン。難しいところはそんなになかったかな。

最近、進捗があまりよくないなぁ。

次章からが本番な気がする。

『HIGH OUTPUT MANAGEMENT』を読んで

去年はあまり本を読めなかったので、今年はせめて毎月 1 冊は何かしらの本を読んでいこうと思う。

一冊目はインテル CEO だったアンドリュー・グローブの『HIGH OUTPUT MANEGEMENT』。過去に何度か読んだことがある本だけど、また読みたくなったので再読。

何度も読んでいるのでさすがに感銘を受けることは少なかったけど、『いかにして自分の組織のアウトプットを高めるか?』という一点に注力して書かれているところに改めて感心してしまった。会議、研修、打ち合わせ、人事考課。日々の仕事すべてを『アウトプットを高める活動か?』という視点で論じていて、姿勢にブレがない。

主に中間管理職に向けて書かれた本だけど、『今日の失敗は、過去のいつかの時点での計画の失敗である』なんてところとか、単なる仕事論としてこの本から学べることも多いのではないかと思う。一方で、二章の組織論の部分は経営層に近い人向けに書かれている気がして、今の自分に直接は関係なかったかな(それでも、やはりミドルマネジャーが重要だという論調だったけど)。

最後にこの本で気に入った一節。

私の1日が終わるのは疲れて帰宅する時であり、仕事が終わった時ではない。私の仕事は決して終わらない。

一見、すごくスパルタな一文に見えるけど、個人的には「疲れたらその日の仕事は終わりだよ」と勝手に解釈している。仕事がうまくいかずに消化不良だった日も、この一節を思い出して「今日は疲れたから仕事は終わり。ゆっくり休んでまた明日。どうせ仕事に終わりはないのだから」と考えて帰路につくようにしている。

CSS Writing Modes Level 3 が W3C Recommendation になった

Web で縦書きを実現するための仕様である CSS Writing Modes Level 3 が長い道のりを経て W3C 勧告となった。

CSS Writing Modes Level 3

最初にこの仕様に関わったのが 2010 年頃。もう 10 年前だね。

当時は大学院生で Firefox の Gecko に実験的に縦書きやルビを実装したりしていた。 その後も数年間はなんとなく関わってテストケースの作成のお手伝いをしたりしていた。

ここ数年は何の関わりもなく見守っているだけだったけど、Acknowledgements に自分の名前を見つけて「少しは貢献できたのかな」と感慨にふけった。

Scheme 手習い(5)

第6章:影法師

numbered?

(define (numbered? aexp)
   (cond [(atom? aexp) #t]
         [(eq? '+ (car (cdr aexp)))
          (and (numbered? (car aexp)) (numbered? (car (cdr (cdr aexp)))))]
         [(eq? '* (car (cdr aexp)))
          (and (numbered? (car aexp)) (numbered? (car (cdr (cdr aexp)))))]
         [(eq? '^ (car (cdr aexp)))
          (and (numbered? (car aexp)) (numbered? (car (cdr (cdr aexp)))))]
         [else #f]))

渡された S 式が (1 + 1) のように演算子が中央にあるような表現式(算術式)かどうかを確認する関数。

aexp がアトムの場合には true。 aexp が演算子で構成されている場合は、それぞれの項が numbered? であるかを再帰的に調べる。

value

(define (value aexp)
  (cond [(atom? aexp) aexp]
        [(eq? '+ (car (cdr aexp)))
          (+ (value (car aexp)) (value (car (cdr (cdr aexp)))))]
        [(eq? '* (car (cdr aexp)))
          (* (value (car aexp)) (value (car (cdr (cdr aexp)))))]
        [(eq? '^ (car (cdr aexp)))
          (expt (value (car aexp)) (value (car (cdr (cdr aexp)))))]

aexp を評価する関数。

numbered? と似たような構造。ちがうのは実際の計算を行うところ。

第7の戒律

【第7の戒律】

性質を同じくするすべての構成部分について再帰すべし。

すなわち、

  • リストの全ての部分リストについて

  • 算術式のすべての部分式について

value2

(define (value aexp)
  (cond [(atom? aexp) aexp]
        [(eq? '+ (car aexp))
         (+ (value (car (cdr aexp))) (value (car (cdr (cdr aexp)))))]
        [(eq? '* (car aexp))
         (* (value (car (cdr aexp))) (value (car (cdr (cdr aexp)))))]
        [(eq? '^ (car aexp))
         (expt (value (car (cdr aexp))) (value (car (cdr (cdr aexp)))))]))

(+ 1 1) のように演算子が先頭にある場合の算術式を計算する関数。

最初の numbered? と異なるのは演算子や部分式を取得する car や cdr の部分のみ。

value3

算術式の表現として次のようなバリエーションが考えられる。

  • (+ 1 1)
  • (1 + 1)
  • (1 1 +)

変わるのは演算子の位置と部分式の位置だけ。 その部分を取得する関数を次のように抽象化できる。

  • 1つ目の部分式を取得する関数:1st-sub-exp
  • 2つ目の部分式を取得する関数:2nd-sub-exp
  • 演算子を取得する関数:operator
(define (value aexp)
  (cond [(atom? aexp) aexp]
        [(eq? '+ (operator aexp))
         (+ (value (1st-sub-exp aexp)) (value (2nd-sub-exp aexp)))]
        [(eq? '* (operator aexp))
         (* (value (1st-sub-exp aexp)) (value (2nd-sub-exp aexp)))]
        [(eq? '* (operator aexp))
         (expt (value (1st-sub-exp aexp)) (value (2nd-sub-exp aexp)))]))

たとえば、(1 1 +) のような表現式の場合にはそれぞれの関数は次のようになる。

(define (1st-sub-exp aexp)
  (car aexp))
(define (2nd-sub-exp aexp)
  (car (cdr aexp)))
(define (operator aexp)
  (car (cdr (cdr aexp))))

第8の戒律

【第8の戒律】

表現から抽象化するに際し、補助関数を使用すべし。

数を空リストで表現する

ここからは数を空リストで表現した場合の演算を実装する。

たとえば、1 は (())、3 は (() () ()) で表現する。

sero?

(define (sero? n)
  (null? n))

zero? に対応する関数。空リストかどうかを調べる。

edd1

(define (edd1 n)
  (cons '() n))

add1 に対応する関数。空リストを一つ増やす。

zub1

(define (zub1 n)
  (cdr n))

sub1 に対応する関数。空リストを一つ少なくする。

+

(define (+ n m)
  (cond [(sero? m) n]
        [else (edd1 (+ n (zub1 m)))]))

足し算を定義する。

ただし、このケースでは lat? の条件を満たさない(たしかにその通りだが、、、)。

タイトルの「影法師」が何の比喩なのかは分からず…。

Scheme 手習い(4)

第5章:*すごい* 星がいっぱいだ

元ネタは『2001 年宇宙の旅』のボーマン船長のセリフらしい。 そういえばそんなセリフもあったな。

rember*

(define (rember* a l)
  (cond [(null? l) '()]
        [(atom? (car l))
         (cond [(eq? a (car l)) (rember* a (cdr l))]
               [else (cons (car l) (rember* a (cdr l)))])]
        [else (cons (rember* a (car l)) (rember* a (cdr l)))]))

アトムとリストの両者を含むリストから特定のアトムを削除する関数。 S 式のリストから、特定のアトムを削除する関数ともいえる。

最後段の cons の第一引数が、(car l) に対する rember* になっているところがポイント。 ここでは (car l) はリストであることが保障されている。

insertR*

(define (insertR* new old l)
  (cond [(null? l) '()]
        [(atom? (car l))
         (cond [(eq? old (car l))
                (cons old (cons new (insertR* new old (cdr l))))]
               [else (cons (car l) (insertR* new old (cdr l)))])]
        [else (cons (insertR* new old (car l))
                    (insertR* new old (cdr l)))]))

S 式のリスト内の任意のアトムの右側にアトムを挿入する関数。

第1の戒律

【第1の戒律(最終版)】

アトムのリスト lat を再帰せしときは、2つの質問、(null? lat) と else を行うべし。

数 n を再帰せしときは、2つの質問、(zero? n) と else を行うべし。

S 式のリスト l を再帰せしときは、3つの質問、(null? l)、(atom? (car l))、else を行うべし。

第4の戒律

【第4の戒律(最終版)】

再帰の間は少なくとも1つの引数を常に変化させるべし。

アトムのリスト lat を再帰せしときは、(cdr lat) を用いるべし。

数 n を再帰せしときは、(sub 1) を用いるべし。

S 式のリスト l を再帰せしときは、(null? l) も (atom? (car l)) も真でないならば、(car l) と (cdr l) を用いるべし。

必ず最終条件に向かって変化すべし。

変化せし引数は、必ず最終条件でテストすべし。 すなわち、cdr を用いるときは、最後に null? で、sub1 を用いるときは、最後に zero? でテストすべし。

occur*

(define (occur* a l)
  (cond [(null? l) 0]
        [(atom? (car l))
         (cond [(eq? (car l) a) (add1 (occur* a (cdr l)))]
               [else (occur* a (cdr l))])]
        [else (+ (occur* a (car l)) (occur* a (cdr l)))]))

S 式のリスト内に特定のアトムが何個現れるかを数える関数。

subst*

(define (subst* new old l)
  (cond [(null? l) '()]
        [(atom? (car l))
         (cond [(eq? old (car l)) (cons new (subst* new old (cdr l)))]
               [else (cons (car l) (subst* new old (cdr l)))])]
        [else (cons (subst* new old (car l))
                    (subst* new old (cdr l)))]))               

S 式のリスト内の任意のアトムを新しいアトムで置き換える関数。

insertL*

(define (insertL* new old l)
  (cond [(null? l) '()]
        [(atom? (car l))
         (cond [(eq? (car l) old)
                (cons new (cons old (insertL* new old (cdr l))))]
               [else (cons (car l) (insertL* new old (cdr l)))])]
        [else (cons (insertL* new old (car l)) (insertL* new old (cdr l)))]))       

S 式のリスト内の任意のアトムの左にアトムを挿入する関数。

member*

(define (member* a l)
  (cond [(null? l) #f]
        [(atom? (car l))
         (cond [(eq? (car l) a) #t]
               [else (member* a (cdr l))])]
        [else (or (member* a (car l)) (member* a (cdr l)))]))

S 式のリスト内に任意のアトムがあるかどうかを判断する関数。

leftmost

(define (leftmost l)
  (cond [(atom? (car l)) (car l)]
        [else (leftmost (car l))]))

S 式のリスト内の最も左のアトムを求める関数。 空リストは考慮しない。

eqlist?

;; 冗長バージョン
(define (eqlist? a b)
  (cond [(and (null? a) (null? b)) #t]
        [(and (null? a) (atom? (car b))) #f]
        [(null? a) #f]
        [(and (atom? (car a)) (null? (car b))) #f]
        [(and (atom? (car a)) (atom? (car b)))
         (and (eq? (car a) (car b)) (eqlist? (cdr a) (cdr b)))]
        [(atom? a) #f]
        [(null? b) #f]
        [(atom? b) #f]
        [else (and (eqlist? (car a) (car b))
                   (eqlist? (cdr a) (cdr b)))]))

;; シンプルバージョン
(define (eqlist? a b)
  (cond [(and (null? a) (null? b)) #t]
        [(or (null? a) (null? b)) #f]
        [(and (atom? (car a)) (atom? (car b)))
         (and (eq? (car a) (car b)) (eqlist? (cdr a) (cdr b)))]
        [(or (atom? (car a)) (atom? (car b))) #f]
        [else (and (eqlist? (car a) (car b))
                   (eqlist? (cdr a) (cdr b)))]))         

S 式のリストが等しいかどうかを確認する関数。 次のパターンの組み合わせで考える。

  • リストが null
  • リストの先頭(car)がアトム
  • それ以外(リストの先頭がリスト)

equal?

(define (equal? a b)
  (cond [(and (atom? a) (atom? b)) (eq? a b)]
        [(or (atom? a) (atom? b)) #f]
        [else (eqlist? a b)]))

S 式のリストだけでなく、アトムが等しいかどうかも確認できる関数。 すなわち、二つの S 式が等しいかを確認する関数。

第6の戒律

【第6の戒律(最終版)】

関数が正しいときのみ簡単化せよ

感想

S 式に対する操作を学習した。

再帰的なリスト(S 式)になるとすぐに頭が混乱していたけど、この章のおかげで自分の頭の中の見通しがよくなった気がする。

(c) The King's Museum