社内数学勉強会 第二回 命題

社内勉強会で、プログラムに使える数学講座が始まりました!
結城浩さんのプログラマの数学っていう本を教材にして、livedoorのエンジニア達が(毎週違う人が担当する)講師をやってくれるという超豪華な社内勉強会です! livedoorは良い会社ですね! ちなみに採用情報はこちらですよ!
ちなみにhamashunは、小学校レベルの数学(と言うか算数)が既に怪しいというかなりアレなレベルの人です! 無事に完走できるかちょっと不安ですね!


さて、第二回(第一回は欠席してしまった><)の今回は、trueとfalseで表される論理に関する内容です。
今回の先生はid:a666666こと刺身さんでした!

論理とは

真か偽かで表す事(語弊がありそうだけど、今回の内容ではこんな感じ)。
これを調べる文を、命題と言う。
そして、命題が正しい時は真(true)であると言い、正しくない時は偽(false)であると言う。

命題の例

  • 6歳より上は、バス料金が100円
  • 6歳未満は、バス料金が0円

この命題には、「もれ」がある。6歳丁度の場合はどちらにも含まれない。

  • 6歳以上は、バス料金が100円
  • 6歳以下は、バス料金が0円

この命題には、「だぶり」がある。 6歳丁度の場合はどちらにも含まれてしまう。

  • 6歳以上は、バス料金が100円
  • 6歳未満は、バス料金が0円

この命題には、「もれ」も「だぶり」もない。


論理演算

以下、A,Bは命題の条件、Tはtrue、Fはfalseの事。

基本
A
T
F

Aという条件に対し、trueの場合とfalseの場合がある。
ちなみにこういう表を真理値表と呼ぶ。

否定
A ¬A
T F
F T

Aがtrueなら¬A(ノットA)はfalseに、Aがfalseなら¬Aはtrueになる。
素人には一瞬わかりづらいけど、「もしAでなければ(が成立する条件)」と考えるとすんなり納得いく。

AかつB
A B A∧B
T T T
T F F
F T F
F F F

AかつB、という命題は、AとBの両方がtrueでなければ成立しないので、T,T,Tが並ぶ1行目のみA∧Bが成立する。

AまたはB
A B A∨B
T T T
T F T
F T T
F F F

AまたはB、という命題は、AとBのどちらか(或いは両方)がtrueならば成立するので、1,2,3行目がA∨Bが成立する。

AまたはB、でも両方じゃない
A B AxorB
T T F
T F T
F T T
F F F

AかBがtrue、でも両方がtrueだったらダメ、という命題なので、2,3行目が成立する。

AとBが等しい
A B A=B
T T T
T F F
F T F
F F T

これは簡単。AとBが同じなら成立するので、1,4行目が成立する。

ちなみに「等しい」をA=Bと表すと、プログラム的に考えるとちょっとした違和感を感じるが、これは数学の勉強なので問題ない。 A≡Bと表す事もある。

AならばB
A B A⇒B
T T T
T F F
F T T
F F T

最後にして最難関。 AならばB、というような命題を含意という。
これの考え方は、「AならばB、が成立するならばtrue」という事。
この考え方は、複数の例を持って行う。


条件

Aの条件
a = 2
Bの条件
a × a = 4


一行目の例

Aの現実
a = 2
Bの現実
a × a = 4

Aの命題がtrue、Bの命題がtrueなら(つまり条件と同じなら)、何も問題なくA⇒Bもtrueになる。


二行目の例

Aの現実
a = 2
Bの現実
a × a = 6

例えAが成立していようとBが成立しなければ、「AならばB」が成立しない。


三行目の例

Aの現実
a = -2
Bの現実
a × a = 4

例えAが成立せずとも結果的にBが成立すれば、「AならばB」が成立する。


四行目の例

Aの現実
a = 3
Bの現実
a × a = 5

これが一番難しい。AもBも成立しないのに、A⇒Bはtrueとなる。
これは誰もが通る難関らしく、僕ももの凄い悩んだんですけど、なんとか理解の尻尾を掴めたような掴めてないような感じになりました。

それは「成立しないという理屈が成立した」という感じ。
詳しく書いてみると、「Aが成立しなければBも成立しないという理屈が成立した」みたいな? うーん、違うかな。。。

サイ本読書記 10章 モジュールと名前空間

10章は、ライブラリなどの製作者向けの情報が多いようです。
名前空間という概念はXHTMLCSSでも存在しているので、わりと読み進めやすかったです。

JavaScript名前空間

JSには、名前空間を定義するための機能が存在しない。しかし、オブジェクトを使って同じ効果を得る事ができる。

var hamashunSpace = {};


hamashunSpace.hoge = function(a){
  var foo = a + 4;
  alert(foo);
}

hamashunSpace.huga = function(b){ 〜 }  // 略



hamashunSpace.hoge(1);  //アラートで5と出る

このコードは、特別な事をしている訳ではなく、単にオブジェクトを生成して関数を格納しているだけ。
以降の関数も、全てこのhamashunSpaceオブジェクトに格納する。つまり、オブジェクト名を接頭辞(prefix)的に使うという事。
これが、ライブラリなどを作る際に守る最もシンプルなルール。

人間の手による名前空間対策

そもそも、何のために名前空間を定義するのかというと、複数のライブラリを併用する際などに変数名などが重複してしまうと動作に不具合が出るから。
これを防ぐためには、前項のようなコードの書き方の他に、人間の手による対策がある。

  • ドキュメントにメモを付ける
  • ファイル名を工夫する

大きく分けてこのふたつになる。

ドキュメントにメモをつける

ライブラリのファイルを開くと、冒頭にコメントアウトされてテキストが記述されている事がよくある。
ここにはオーサーやライセンスなどの情報がある事が一般的だが、合わせて名前空間的なトラブルを避ける為の情報を書いておくというもの。

/*
 * hamashunSpace.js
 *
 * このファイルではhamashunSpaceというグローバルシンボルを定義します
 * 全ての関数はこのhamashunSpaceオブジェクト内に格納します
 * 
*/
ファイル名を工夫する

オブジェクト名を独自の物にする事で重複の可能性はかなり下がるが、それでも完璧ではない。そのため、ファイル名とオブジェクト名を同じものにする。
例えば複数のファイルを必要とするライブラリを作成する場合には、名前空間用のオブジェクト名をファイル名と揃えるというルールを課す。

同じファイル名のファイルは同じディレクトリに存在できないので、結果として重複を防ぐ事ができる。もし同じファイル名にする必要がある場合は、ディレクトリを分ける事になる。
その際は、オブジェクト名の方を変更する

var one.hamashunSpace = {};  // oneフォルダの中にhamashunSpace.jsファイルがある感じ

var two.hamashunSpace = {};  // twoeフォルダの中にhamashunSpace.jsファイルがある感じ

実際にライブラリを作る際

var hamashun;
if(!hamashun) hamashun = {};
hamashun.hoge = {};

hamashun.hoge.define = function(a,b){ 〜 }
hamashun.hoge.prop = function(123){ 〜 }

前半のコードは、調査用。
hamashunというグローバルなシンボルをまず宣言し、ifで未定義ならばオブジェクトを生成。
んで、hamashun.hogeオブジェクトを生成し、これを名前空間用に使う。

※シンボル、という言い回しが急に出たので調べてみたんですが、どうやら関数とか変数とかをざっくり表すもの?みたいです。宣言はしてるけど定義はされてないものを示すために使われているのかな。


なぜここで

var hamashun = {};
if(!hamashun) hamashun.hoge = {};

としないかというと、宣言されていないグローバルシンボルを呼び出すと例外になるため。
下記が検証用コード。

// アラートはunderfined
var hamashun;
alert(hamashun);

// アラートは[object Object]
var hamashun2 = {};
alert(hamashun2);

下の方のコードでunderfinedにならないのが「例外」って事…なのかな。 多分。
サイ本の解説でhamashun.hoge.propのようにふたつの段階を踏んでそれからプロパティを定義しているのは、その方が重複しない可能性が上がるからのよう。

オマケ JAVAでの方法

JAVAではドメインを利用して接頭辞にする方法があるらしい。もちろんJSでもやろうと思えば可能。
そういえばGreasemonkeyにもありますね。

サイ本読書記 9章 クラスとコンストラクタとプロトタイプ(後半

プロトタイプオブジェクトは、前半で示したようなユーザー定義クラスだけでなく、組み込みクラスにも継承されている。そして、そこにプロパティを追加する事もできる。
ただし、他の人がさわった時に混乱するので、普通はやらない。例外として、古いブラウザに対応するために使う事はある。

また、Object.prototypeは全てのクラスに影響を及ぼすので、プロパティの追加は絶対に行わない事(とサイ本曰く)。

オブジェクトとプロトタイプのまとめ

オブジェクト

名前がついたデータを、格納するもの。
データと聞くとメソッド(というか関数)は含められなさそうな気がするけど、含められる。JSは関数もデータ。
オブジェクトでもって関連した値やメソッドをひとまとめにする事で、プログラミング作業の効率を上げる事ができる。
JAVAC++は強く型付けされた言語なのでプロパティはあらかじめ定義されていてデータ型も決められている。
JSはその辺が自由で、動的に変更する事もできる。

クラス(のようなもの)(コンストラクタ関数とプロトタイプオブジェクト

クラス(コンストラクタ関数)はオブジェクトの設計図のような物で、コンストラクタ関数を使って生成されたオブジェクトは、そのコンストラクタのインスタンスであると言える。
プロトタイプオブジェクトは全てのオブジェクトから参照される。そしてプロトタイプオブジェクトのプロパティは、全てのオブジェクトに継承される。
コンストラクタ関数でプロトタイプを使うと、そこから生成される全てのインスタンス(オブジェクト)に共有したプロパティが生成される。


JAVAの用語をJavaScriptに無理やりあてはめた説明

(この辺、本当に無理やりな気がするんですけど、実際に使ったりするんでしょうか? 呼び名は無理やりでも、実際のコードしては便利なのかな。
あと、サイ本のサンプルコードは省略された物しかなかったので、自分なりに補って書いてみました。 超自信ない><)

インスタンスプロパティ

インスタンス(コンストラクタ関数によって生成されたオブジェクト)の持つプロパティの事。
コンストラクタ関数を使って生成されたオブジェクトが10個あれば、それぞれに存在する。
それぞれのオブジェクトを介して各インスタンスプロパティにアクセスする事ができる。

function Rect(w,h) {
 this.width = w;
 this.height = h;
}

var rec1 = new Rect(5,10);
var rec2 = new Rect(99,999);

rec1.width 〜
rec2.width 〜

rec1、rec2オブジェクトそれぞれのインスタンスプロパティへのアクセス

この仕組みはJSが少し特殊で、従来のクラスベースのオブジェクト指向言語では、また理屈が違うらしい。


インスタンスメソッド

名前から想像できるけど、インスタンスが持つメソッド。
アクセスする時は、そのオブジェクトを介して行う。

function Rect(w,h) {
 this.width = w;
 this.height = h;
}

var rec1 = new Rect(5,10);
rec1.area = function(){ 〜 }

var a = hoge1.area():
alert(a);

アクセスされたメソッドは、thisキーワードでもって対象のインスタンスを参照する(どのインスタンスから呼ばれたのか)。
このthisはJSでは必須。JAVAなんかだと省略できるらしい。
まあでも、前半の内容でメソッドはインスタンスではなくクラス側に書こう、という内容があったけど、どうなんだろう。使い分ける理由とかあるのだろうか。

他のクラスベースのオブジェクト指向言語では、インスタンスメソッドはそもそも共有できる、という仕組みらしい?

クラスプロパティ

クラスに存在するプロパティ。インスタンスがいくつあっても、クラスプロパティはひとつだけ。
JSでクラスと言うとコンストラクタ関数なので、つまりコンストラクタ関数にプロパティを設定すればよい。

function Rect(w,h) {
 this.width = w;
 this.height = h;
}

Rect.UNIT = new Rect(5,100);

コンストラクタ関数はその名の通り関数だけど、JSの関数はオブジェクトなのでプロパティを設定する事ができる。
インスタンスプロパティにアクセスする時にはインスタンスのオブジェクトを介したけど、クラスプロパティにアクセスする時はクラスを介してアクセスする。

クラスメソッド

クラスに設定され、クラスを介して呼び出されるメソッドの事。クラスを介するという事は、コンストラクタ関数を介して呼び出すという事。
もしthisを使うと、そのthisはインスタンスではなくクラスメソッド自身を指す(オブジェクト生成の時はインスタンスを指す)。
クラスメソッドとクラスプロパティはグローバルな存在なので、上書きされる事がないというメリットが存在する。

function Rect(w,h) {
 this.width = w;
 this.height = h;
}

Rect.CONTAINER = function hoge() { 〜 }

ダックタイピング

「アヒルのように歩いて、アヒルのように鳴くなら、それはアヒルである」という、アメリカの古い格言に基づいた考え方。
JSをこれにあてはめると、「fooというオブジェクトがhogeというクラスのメソッドを実装していれば、fooはhogeクラスのインスタンスである」となる。
JSは弱い型付けの言語なので、例えhogeコンストラクタ関数から生成されたインスタンスでなくても、同じ結果を持つ物ならhogeインスタンスとして扱う事ができる。

これの深い部分は今の自分にはマニアックすぎると感じたので、省略。

サイ本読書記 9章 クラスとコンストラクタとプロトタイプ(前半

9章です。9章は難しい上にページ数が多いので、前半と後半に分けたいと思います。 ほんと難しいので、後半は今の時点ではスルーするかもしれません。

9章は、JavaScriptを書く上の必須知識というよりは、オブジェクト指向的に書くにはどうするのかという内容かなと感じました。 そういえばこの前書いてみたJS(ぽいもの)を先輩に「オブジェクト指向ぽくないな」と言われました。

コンストラクタ関数

新しく生成したオブジェクトの初期設定をする関数。初期設定とはプロパティに値を設定するなど。

// コンストラクタ関数
function Rect(w,h) {
 this.width = w;
 this.height = h;
}

// オブジェクトを生成する時にコンストラクタ関数で設定
var rec1 = new Rect(5,10);
var rec2 = new Rect(99,999);

これで以下のようなオブジェクトになる。

rec1 = {width:5, height:10};
rec2 = {width:99, height:999};

new演算子でオブジェクトを生成する時に、引数をコンストラクタ関数に渡している。

クラスとインスタンス

正確にはJSにクラスは無いけど、他に呼び方もないしクラスっぽいからクラスって言うよというのがサイ本の記述。
コンストラクタ関数がクラス、生成されたオブジェクトがインスタンス

クラスとインスタンスはしばしば設計図とそれを元に作られたモノに例えられるけど、そんな感じらしい。コンストラクタ関数が設計図で、生成したオブジェクトがモノ。
一度コンストラクタ関数を書いておけば、後はオブジェクトを生成する時に引数を渡すだけでよい。だから、似たオブジェクトをたくさん作る時とかに効率とか良い。

なお、コンストラクタ関数の命名には生成するオブジェクトの名前を付けるのがお勧め、との事。つまりオブジェクトに合わせて関数名を決めるという事。

プロトタイプと継承

function rect(r) {return r.width * r.height;}

というコードは、動くことは動くがオブジェクト指向的では無い、らしい。

var r = new Rect(50,100);
r.area = function(){return this.width + this.height;}

var a = r.area();

とすればオブジェクト指向的だけど、メソッドを実行するためにメソッドをオブジェクトに追加しているのがちょっと無駄。
更にこれを書き直すと、

function Rect(w,h) {
 this.width = w;
 this.height = h;
 this.area = function() {return this.width * this.height;}
}

var r = new Rect(50,100);
var a = r.area();

こうなる。処理はコンストラクタ関数にまとめてしまう、みたいな感じ?確かにこっちの方がすっきりして解りやすい気がする。
オブジェクト指向はプログラミングをラクにする為の物らしいので、コードを直感的にする事は大事なんじゃないかと思った。

でも、サイ本はこれでもベストでは無いと言う。
なぜなら、このコンストラクタ関数を使う全てのインスタンスがメソッドも含めてしまうから。

this.widthとthis.heightの二つのプロパティは、値がwとhとなっている事からも分るように、インスタンス毎に値が異なる。なのでこれは問題ない。
でもthis.areaは関数が記述されているだけなので、どのインスタンスでも同じ内容になる。これは非効率的。

この問題を解決するために、プロトタイプというものを使う。

プロトタイプ

JSの全てのオブジェクトは、プロトタイプオブジェクトを参照できる。
プロトタイプオブジェクトのプロパティは、プロトタイプオブジェクトを参照するオブジェクトのプロパティとして使える。つまり、プロトタイプオブジェクトのプロパティは全てのオブジェクトで使う事ができる。

function Rect(w,h) {
 this.width = w;
 this.height = h;
}

var rec1 = new Rect(5,10);
var rec2 = new Rect(99,999);

上記のコードでオブジェクトを生成すると、流れ的には

  1. new演算子で空のオブジェクトを生成
  2. new演算子が空のオブジェクトに対してプロトタイプを設定
  3. コンストラクタ関数の処理が行われる
  4. rec1とかrec2とかになる

となる。

2で設定されるプロトタイプとは、コンストラクタ関数のprototypeプロパティの値。
prototypeプロパティとは、全ての関数が定義時に自動で生成されるプロパティ。値も設定される。
prototypeプロパティの初期値は、constructorプロパティのみを持つオブジェクト。
constructorプロパティは、コンストラクタ関数自身を参照する。

インスタンスごとに変わらないものにはプロトタイプを使う
function Rect(w,h) {
 this.width = w;
 this.height = h;
}

Rect.prototype.area = function() { return this.width * this.height;}

var rec1 = new Rect(5,10);
var rec2 = new Rect(99,999);

コンストラクタ関数はクラスに当たり、widthやheightのプロパティを設定する。
コンストラクタ関数で設定されたオブジェクト(rec1やrec2)は、コンストラクタ関数に関連付けられたプロトタイプオブジェクト(Rect.prototype)から同じプロパティ(area)を継承する。
この継承は自動で行われる。
このような書き方をすると、メモリが節約できる。

三項演算子について

はてダ久しぶりです。
最近できた社内ブログが便利かつ楽しすぎて、そっちばっかり更新していました><
会社に凄い人たちが多すぎて、そんな人たちが即ツッコミ入れてくれたりするというステキ環境なので…!

さっき三項演算子について習ったので、久しぶりにこっちにもポストします。

三項演算子とは

Wikiペディアで調べると

プログラミングにおける三項演算子とは、3つの引数をとる演算子のことである。

とか書いてあります。 全然わからないです>< Wikiペ難しい!><

そこで会社の人に聞いたところ、

if ( hogehoge ) { hoge } else { fuga }
を短くかけるというやつだよ

と教わりました! 凄い! 一瞬で解った!

書式

:と?をセットで使う。
上記のコードを三項演算子で表すと、

hogehoge ? hoge : huga;

となる。
条件 ? trueの時 : falseの時
みたいな。 ああ、理解してからWikiペディアを読むと解りやすいなあ。

おまけ的な

会社の人から、オマケ的なサンプルコードを教わりました。
これはかなりやりすぎで、実務というよりは極端な例みたいなやつ、との事です。

a ? b ? d : e : f ? g : h;

意味が解らなかったんですけど、malaさんが以前三項演算子について書いていた事を教えてもらって、その法則で書き直してみる事にしました。
それが以下です。

(a)? (b) ? d : e : (f) ? g : h;

微妙にわかりやすくなったような気がしないでもないです。
でもこのサンプルだったらこうした方が解りやすいかな、って思って直したのが以下。

(a? (b ? d : e) : (f ? g : h));

結構わかりやすくなったかな、と思いました。

サイ本読書記 8章 関数

ここ暫く時間が取れなかったサイ本読書記ですが、ようやく8章を読めました。
8章は関数について詳しく解説しています。

組み込み関数とユーザ定義関数は同じ感覚で使える

…というのは利点な気もするけど、超初心者の頃は例によって分りづらかった。 用意されてる物とそうでない物がごっちゃになって混乱してた。

return文

そもそもreturn文は必須ではない。
return文の役割は、その時点で処理を中断して値を返す。

例えば

function print() {
 var n = 3;
}

document.write(print("hoge" + print));

とするとundefinedになる。

function print() {
 var n = 3;
 return n + 2;
}

document.write(print("hoge" + print));

のようにreturnで返す値を指定しておけば、返る値で処理ができたりする。。。って事なのかな><
ちなみにこの結果は5になる。

関数の入れ子

function文で定義した場合、関数の入れ子はifやwhileの中では定義できないらしい。
よくわからなかった。

関数リテラル

function hoge() {}  // function文
var hoge = function() {}  // 関数リテラル
匿名関数

関数リテラルは匿名関数を生成する。 匿名関数とは言っても関数名を付ける事ができる。

var hoge = function foo() {}  // 匿名だけど名前がついてる関数

ちょっと分りづらいんだけど、関数リテラルの時点で匿名関数である事が決まっていて、関数名の有無は匿名かどうかには関係ないようだ(日本語の文言的なアレで混乱しやすい)。 名前を付けておくと再起呼び出しの時に便利。

再起呼び出し
var hoge = function foo(n) {
 if(n == 1) {
  document.write("1");
 } else {
 document.write( n * foo(n + 1));
 }
}

サンプルコードがちょっと変かも><
再起呼び出しって言うのは、Aっていう関数の中でA自身が登場する事。 プログラミング全般的な用語。

関数の命名規則

  • アンダースコアやキャメライズが一般的
  • 内部でのみ使う関数や不可視にしたい関数はアンダースコアから始める名前にする慣習があるらしい

関係無いですけど、Perlはアンダースコアで繋ぐのが一般的なんだとか。 HTMLとCSSだとキャメライズが最近は多いのかな。 そういう言語ごとの文化みたいのも色々と面白そう。

引数の数について

JSでは、引数は増えても減ってもいい(エラーにならいという意味で)。 ただし無視されたりする。
例えば

function hoge(msg, n, k) { }

のような関数を呼び出す時に、

hoge("ほげほげ");

と1つしか引数を指定しなかった場合、残りの二つの引数は未定義値が設定される。 また逆のパターンも可能で、

function hoge(msg) { }

のような関数を呼び出す時に、

hoge("ほげほげ", 100, 0325);

とした場合、この場合で言うと後の二つの引数は無視される。無視の基準は単に順番。

Argumentsオブジェクト

関数に定義された引数より多い引数を渡しても通常は無視されるが、Argumentsオブジェクトのargumentsプロパティで参照する事ができる。 Argumentsオブジェクトは「配列のような」オブジェクト。

function hoge(h, j, k) {
 document.write(h + j + k);
}

hoge(1,2,3,4,5);

関数hogeに渡す引数1,2,3,4,5の内、4と5は無視される。 しかし

function hoge(h, j, k) {
 document.write(arguments[4]);
}

hoge(1,2,3,4,5);

とすると5番目の引数(つまり5)を参照する事ができる。 つまり、関数に定義された数以上の引数を渡した場合、溢れた引数は消滅している訳ではない。

配列のようなオブジェクト
// 配列
function hoge(n) {
 a = [];
 a[0] = n;
 n = 2;
 document.write(a[0]);
}

hoge(1);

とすると、出力されるのは1のまま。

// 配列のような
function hoge(n) {
 arguments[0] = 2
 document.write(arguments[0]);
}

hoge(1);

とすると、出力されるのは2になる。

lengthプロパティ

function hoge (h,j,k,l) {
 document.write(hoge.length);
}

hoge(3, 100);

とすると、関数に宣言された引数の数を参照する。 ここでは4が出力される。

function hoge (h,j,k,l) {
 document.write(arguments.length);
}

hoge(3, 100);

とすると、関数に渡された引数の数を参照する。 ここでは2が出力される。

開発サーバとSVNのサーバとローカルの事


例えが適切でないと言われたので補足追加

会社のある開発用サーバの容量が一杯一杯になってて、その理由が『退職した人の作業用領域が残ったまま』だったり『自分の担当でないファイルまでcheckoutしてるから』だったらしい。そのサーバはイントラにも使われていたので、重くなったり使えなかったりしてた。僕も入社したばっかりの時に手当たり次第にcheckoutしてソースを見てたりしたので、不安になって色々教えて貰った。

前提〜3つのコンピュータ〜

開発用サーバと、バージョン管理用サーバと、個人ごとのPC(いつも直接使ってるやつ)

checkoutするとコピーができる

バージョン管理用サーバからcheckoutするとcheckoutしたマシン(ここでは開発用サーバか個人ごとのPC)にコピーのファイルが作られる。これはユーザ毎に作られるので、開発用サーバでcheckoutしたらした人の数だけ容量が圧迫されていく。

サーバとクライアント

サーバとクライアント、という呼び方は立場によって変わる。個人用PCから開発用サーバを見ると開発用サーバはサーバだけど(ちょっと不適当かも)、バージョン管理用サーバから開発用サーバを見ると開発用サーバはクライアント。
例えて言うなら、レストランの店員は勤務中はサーバだけど、休みの日に別の店にご飯を食べに行ったらクライアントになる。みたいな。
■追記
サーバかクライアントか、というのはPCごとに決まるのではなく、アプリケーションごとに決まる。 例えがおかしかったので追記。

分かりにくかったところ

開発用サーバが、『サイト領域』と『開発用の作業場』を兼ねていたという事。


動きまとめ

僕が普段やってる事の動きはこんな感じだった。
クライアントPCで作業→バージョン管理サーバにコミットして新しいバージョンにする→作業用サーバにsshでログインして「you、最新バージョンを反映しちゃいなYo」って命令→開発用のサイトに反映される

結論だけど余談

僕は個人ごとのPCでcheckoutしてたんで、サーバを圧迫はしていませんでした! おしまい。