2016年5月25日 星期三

怎麼會是我

怎麼會是我


我努力地回想 
但仍想不起為什麼會在這裡走來走去, 不知多大的空間, 無盡地黑。

自己也搞不清楚怎麼回事, 明明那樣的黑, 卻仍能"看得到"(或感覺到)些許移動的暗影,
使我想起了以前某回社團活動-夜半無月的荒野試膽大會,
或許是星光, 又或許史前祖先遺留下來的求生本能,
雖無燈無月, 但仍能由不同程度的黑暗對比出人影或樹影, 尤其當有人移動或風吹樹動時。 

除了大廳外, 隔出一間間奇怪的小房間-門口沒有門, 窗口半扇窗,
我來回疾走掃描了會兒便往某間坐下, 見到窗門外一堆暗影輪廓不停地來去匆匆,
看來有不少人跟我一樣搞不清狀況。

"這裡是什麼地方, 為什麼我在這裡?" 相同的疑問此起彼落, 黑暗中開始有了嗡嗡聲, 打破原本的死寂

"這裡是地獄" 左手邊有位仁兄開口說道, 看來他於此處已有多時。
大夥聽到他的話, 死寂重新掌握黑暗片刻, 恐懼開始蔓延

右前方圍著一堆美眉, 因他的話而開始騷動起來, 讓我更加不安。

"地獄! 開玩笑, 那邊不是有大門可以出去嗎" 我駁斥, 手指著大廳前方

"你出去看看, 馬上有一堆鬼怪用它們的尖牙利爪撕爛你!" 他撩起衣服秀出那傷痕累累的肚皮。
由於受創極大, 不規則傷口極度地凹凸不平
"像是魔鬼的雕塑品" 他調侃著自己的肚皮, 即使如此黑暗的環境仍能感覺到這魔鬼雕塑的深刻鑿痕
"受到如他百分之一的傷害, 活人也變成死人" 我心想。他肚上的撒旦藝術說服了大家
"若這樣痛苦真能死去讓我離開這鬼地方倒也還好。" 他用手遮掩臉上痛苦的表情繼續說道。
"問題是, 每次被撕爛後又會回到這鬼地方, 只留下這一身愈來愈多的傷疤" 雖然痛苦, 看來他還是常忍不住試著逃跑。
這使我想到了南方四賤客的阿尼, 阿尼的不死之身使他每次死後必從自己的房間醒來, 
差異只有阿尼死法較peace, 復活不留傷疤。 
即使回憶起有趣的卡通也無法引起我絲毫的笑意, 
那肚皮傷口是刻意留下的, 不是當作他勇於嘗試的證明, 而是警告
警告他, 也警告這裡的每個人。

那堆美眉因恐懼開始歇斯底里地尖叫
我雙手環著自己, 思緒凌亂, 想抓住什麼


"怎麼會是我?是因為我知道週遭有些人可能下地獄而沒提出警告嗎?" 我陷在思考的漩渦。
"例如四樓那位長年肺炎的租房鄰居, 經常性刻意咳嗽把其他鄰居嚇到搬家。不用看他命盤, 就知道他日後八成將因長期肺炎得肺癌。死後下地獄是他自然的狀態"
"可我有告訴他要帶口罩呀!是他執意不帶, 故意咳嗽嚇人。但我也盡量讓心中不起怨念。怨念是瓜葛, 我可不想跟這樣的人有絲毫瓜葛。"
"又為什麼不是那一堆禍國殃民又愛拿錢的政客在地獄, 怎麼會是我?"
歇斯底里的美眉中突然有位衝出來抱住胡思亂想的我
當下恐慌的自己自然而然地抱住她

原來真有心意相通, 沒有言語 
擁抱的兩人互相感應到彼此心靈逐漸平靜, 有了片刻永恆的安寧

不知過了多久, 我倆結束了擁抱, 才發覺那堆美眉仍在嘶力尖叫,
一方面受不了她們的尖叫聲, 一方面也好奇剛剛心意相通的體驗,
我試著朝其中一位尖叫不停的美眉抱去

尖叫持續著她的恐懼, 發覺到擁抱沒效果, 她跳開遠離
"這樣也好, 噪音變少了" 我心想。 於是對其他美眉採取同樣的行動模式

其他美眉也只持續著恐懼尖叫而遠離

周遭又再寂靜起來, 總算使我能再靜下思考

"那麼多美眉才一個心意相通, 怪不得談戀愛不容易, 離婚率那麼高" 沒實際戀愛相處經驗的我似有所悟

"剛剛心想那些人死後上刀山下油鍋等是他們的自然狀態; 那我目前會在這地獄難道不是相同道理?"

"我討厭地球人, 不喜歡跟人相處, 眼下這無盡的黑暗不也正是我的自然狀態嗎?" 想通後隨即釋然

他人的年少輕狂時期自個兒在那to be or not to be.
暗忖自己若自殺後下的地獄或許類似那科幻片"失落的地平線"裡地獄場景,
或可勉強維持片刻理性, 思考些許數理邏輯, 所以那時數理學得還滿勤的

"其實目前這地獄已比我先前預想會下的層數好很多, 好在沒自殺" 黑暗中自我安慰

不知過了多久, 眼前突然出現夕陽黃昏

"放風一下下, 等會兒自動回去" 先前告知地獄的那位仁兄跟著我一起放風

我在某麵攤前聞一下後便離開, 麵攤老板始終看不見我, 自顧著下麵
我在世時食欲本不高, 原來死後也差不多

"現在的我, 坦然無懼, 等會兒回到地獄, 還有機會抱著她嗎?" 看著夕陽, 我心想



後記:
不知現在標點怎麼扣分, 沒怎麼注意標點的正確性
不過在考試的時間限制下, 我考試時應寫不出這篇

此作很個人, 鮮少人具相似經驗能體會吧 
因此若真拿給老師打分數, 即使不挑錯別字與標點符號, 我想分數也不高

2016年3月9日 星期三

mongodb howto

前言:

前幾天看中視60分鐘談雲端談大數據
使己想找找去年作的mongodb筆記
竟没有在記憶中的目錄
一度以為是因年初換硬碟時 有操作失誤而錯漏
心多跳了一下下, 覺得還是在網上放一份較保險

不過對節目裡那郭中研院的講要求資料100%正確...有點怪怪的(或許是剪輯造成的誤解),
對資料一致性的要求因雲端而從 傳統ACID的強一致 跳到 BASE的軟狀態與最終一致(or弱一致) 
在很多情況, 依照使用情形的不同, 在當下取用狀態就是不一致 i.e.非100%正確
例如一些社交性的軟體在某些日常社交資料, 大數據統計某些偏好趨勢等

簡而言之,  資料正確百分百 不算是 雲端資料庫 的必要條件
                                                                                                           2016/3/9
-----------------------------------------------------------------------------------


MongoDB - 較適合對資料庫與程設已有基礎者


MongoDB 屬NoSQL(Not only SQL, 雲端常用)中的 文件資料庫
MongoDB的collection 由 文件(documents) 組成, collection 類似 關聯式資料庫的table
雖亦有field(s), 但不為其預先定義schema
如多數 文件資料庫 般支援JSON格式; 亦同時支援二進位的BSON格式

安裝:
urpmi mongodb # 安裝 client
urpmi mongodb-server # 安裝 server

設定:
最原先的mongodb是將資料預設存於 /data/db/
然所用的rpm已改預設至 /var/lib/mongo/ 中 (設定檔 /etc/mongod.conf 內可改預設)

啟動server: <註一>
systemctl start mongod.service # 舊法 service mongod start 或仍可用

預設為單機,故 netstat -antup 有類似下行顯示其中:
tcp        0      0 127.0.0.1:27017         0.0.0.0:*               LISTEN      29100/mongod


mongo - MongoDB Shell: mongo is an interactive JavaScript shell interface to MongoDB  <ref.1>
啟動 mongo - 於同上已執行server的主機上執行 mongo 進入 MongoDB Shell 來與上面啟動之server交談:
> show dbs // 檢視現有資料庫       '>' 為 MongoDB Shell 的提示符號
> use <資料庫名> // 切換資料庫
> exit // 離開 MongoDB Shell

注意!最初啟動時有行訊息為:connecting to: test  表預設連接至 資料庫名test之資料庫
      但用 show dbs 時卻看不到 test資料庫 的存在,這是因為mongodb的資料庫是自動(隱式)建立,
      多是當第一次揷入資料物件於collection時, 自動建立資料庫與collection
      最初雖然已切換至 test資料庫, 然而由於尙未執行操作,此時 test資料庫 實際上並不存在,
      故用 show dbs 自然看不到它; 同理以 use 來切換資料庫時亦然
      而collection與index的情況與上類似 e.g.當第一次對collection操作時自動建立collection; index亦然

底下皆在 MongoDB Shell 中操作,一些例參考<ref.2>:
// 於Shell內可用諸如help/db.help()/db.mycoll.help()/db.mycoll.find().help()等等來參考說明
// 當直接輸入省略()的方法名,會出現其方法的原始程式,亦為不錯的參考資訊
> use mydb // 不用預設資料庫test, 切換至 資料庫mydb 
switched to db mydb

> j={name:"mongo"} // 建立一資料物件j,  其中field名為name, field值為mongo (<自>亦可視field為資料物件的屬性)
{ "name" : "mongo" }

> k=j             // 由於是 JavaScript shell, 故j,t的內涵實則是指向資料物件的指標(pointer), 故 k與j指向同一個資料物件
{ "name" : "mongo" }

> k.name="hahaha" // 將 name屬性 的值 改成hahaha   注意:(資料)物件的屬性 亦即為 資料物件field
hahaha

> j // 因隱含性質是指標(pointer), 故j與k的顯示的結果相同
{ "name" : "hahaha" }

> k.name="mongo" // 改回 "mongo"

> j // 結果一様與k同
{ "name" : "mongo" }

> show dbs // 檢視現有資料庫, 因上面所建資料物件尙未儲存, 故資料庫mydb尙未建立  
local  0.078GB // 注意 0.078GB 僅為參考, 會因版本等相異情況而有所不同

> db.things.save(j) // 將j的資料物件插入至collection things中

> show dbs // 由此可知,上一指令除建立collection things外,亦自動建立 資料庫mydb 
local  0.078GB
mydb   0.078GB

> show collections      // 顯示當下用的資料庫(此處為mydb)中有那些collections
system.indexes // <--- 此collections即是 系統為於此 資料庫mydb 自動建立index的存放處 (index結構為B-tree)
things

> t={x:3} // 建立一資料物件t  注意!因t,j為local, 故exit後t,j定義消失, 即使作了底下的db...save()亦然
{ "x" : 3 } // 注意一開始定義時只有屬性x (亦即field x), 尙無field _id

> db.things.save(t) // 將資料物件t增至collection things 中
WriteResult({ "nInserted" : 1 })

> db.things.find() // 顯示collection things 的 documents <註二>
{ "_id" : ObjectId("..........略............"), "name" : "mongo" }
{ "_id" : ObjectId("556b87c341e6e4838babbdc1"), "x" : 3 }

> t // 即 t 於上面.save(t)後 方有field _id, 且_id與以.save(t)增添至things內的document所產生的_id相同
{ "x" : 3, "_id" : ObjectId("556b87c341e6e4838babbdc1") }
                         // 若前面是用db.things.insert(t), 則上行仍只為 { "x" : 3 }

> use test // 切換至 資料庫test
switched to db test

> db.things.save(t) // 類似上,只不過現在於 資料庫test上操作, 此行亦自動建立 資料庫test與其內的colleciton things 
WriteResult({ "nInserted" : 1 })

> db.things.find() // 由於已有 t._id 故.save(t)時以沿用其_id來增添document (注意與 mydb內document 為個別的實體)
{ "_id" : ObjectId("556b87c341e6e4838babbdc1"), "x" : 3 }

> show dbs
local  0.078GB
mydb   0.078GB
test   0.078GB

> db.things.insert({x:7}) // 效果同 db.things.save({x:7})  亦相當於SQL的 INSERT INTO things VALUES (7);
WriteResult({ "nInserted" : 1 })

> db.things.find()
{ "_id" : ObjectId("556b87c341e6e4838babbdc1"), "x" : 3 }
{ "_id" : ObjectId("556b87c341e6e4838babbdc2"), "x" : 7 }

       // 注意! 下行remove()中若未加參數或以空文件{}當參數, 則會將所有collection中的documents全部刪除! 用時須謹慎
> db.things.remove({x:3}) // 類似於SQL的 DELETE FROM things WHERE x=3; (另外,可利用field _id刪除所指定document)
WriteResult({ "nRemoved" : 1 })

> db.things.find() // 顯示結果 資料庫test的 collection things 中 只剰一document
{ "_id" : ObjectId("556b87c341e6e4838babbdc2"), "x" : 7 }

> db.things.remove({x:7}) // 相當於SQL的  DELETE FROM things WHERE x=7;  即當中有field x為7者皆刪除
WriteResult({ "nRemoved" : 1 })

> db.things.find() //  資料庫test的 collection things 中已無資料

> show collections // 然而 資料庫test 中的things仍存在,顯示 雖mongo會自動建立collection,但不會自動刪除
system.indexes
things

> db.things.drop() // 於資料庫test中 刪除collection things
true

> show collections // 資料庫test中顯示已無things
system.indexes

> show dbs // 同上理 資料庫test 仍存在
local  0.078GB
mydb   0.078GB
test   0.078GB

> db.dropDatabase() // 刪除目前所在資料庫  i.e.資料庫test
{ "dropped" : "test", "ok" : 1 }

> show dbs // 顯示已無 資料庫test
local  0.078GB
mydb   0.078GB

> use mydb // 切換回到 資料庫mydb 
switched to db mydb

> for (var i=1; i<=20; i++)   db.things.save({x:4,j:i})  // 利用JavaScript的for loop來增添資料, 注意此處x,j是field名
WriteResult({ "nInserted" : 1 })

> db.things.find() // 因資料過多而只顯示前面部份
{ "_id" : ObjectId("..........略............"), "name" : "mongo" }
{ "_id" : ObjectId("556b87c341e6e4838babbdc1"), "x" : 3 }
{ "_id" : ObjectId("..........略............"), "x" : 4, "j" : 1 }
......
{ "_id" : ObjectId("..........略............"), "x" : 4, "j" : 18 }
Type "it" for more
> it // 打 it 方接續顯示上面未顯示的部份
{ "_id" : ObjectId("..........略............"), "x" : 4, "j" : 19 }
{ "_id" : ObjectId("..........略............"), "x" : 4, "j" : 20 }

> db.things.find().limit(5).skip(2)   // 跳過前兩筆後顯示五筆; 相當於SQL的 SELECT * from things LIMIT 5 SKIP 2; 
{ "_id" : ObjectId("..........略............"), "x" : 4, "j" : 1 }
{ "_id" : ObjectId("..........略............"), "x" : 4, "j" : 2 }
{ "_id" : ObjectId("..........略............"), "x" : 4, "j" : 3 }
{ "_id" : ObjectId("..........略............"), "x" : 4, "j" : 4 }
{ "_id" : ObjectId("..........略............"), "x" : 4, "j" : 5 }

> db.things.find({x:3}) // 相當於SQL的 SELECT * from things WHERE x=3;
{ "_id" : ObjectId("556b87c341e6e4838babbdc1"), "x" : 3 }

> db.things.find({x:3},{_id:1}) // 類似上一個結果,然只顯示field _id
{ "_id" : ObjectId("556b87c341e6e4838babbdc1") }

> db.things.distinct("x") //  相當於SQL的 SELECT DISTINCT x from things;
[ 4, 3 ]

> db.things.find({$or:[{name:"mongo"},{x:3}]})     // 相當於SQL的 SELECT * from things WHERE name="mongo" OR x=3;
{ "_id" : ObjectId("..........略............"), "name" : "mongo" }
{ "_id" : ObjectId("556b87c341e6e4838babbdc1"), "x" : 3 }

> var tstcursor=db.things.find() // 宣告變數tstcursor為 db...find()傳回的object cursor

> while (tstcursor.hasNext())    printjson(tstcursor.next())    // <註三>
{ "_id" : ObjectId("......略......"), "name" : "mongo" }
......
{ "_id" : ObjectId("......略......"), "x" : 4, "j" : 20 }

                                        // 下行相當於SQL的  SELECT * from things;
> db.things.find().forEach(printjson) // .forEach(func)為cursor method 相當於上兩行scripts的總效果
{ "_id" : ObjectId("......略......"), "name" : "mongo" }
.....
{ "_id" : ObjectId("......略......"), "x" : 4, "j" : 20 }

> db.things.find({x:{$ne:4}}) // 排除field x為4的documents; 相當於SQL的  SELECT * from things WHERE x!=4;
{ "_id" : ObjectId("..........略............"), "name" : "mongo" }
{ "_id" : ObjectId("556b87c341e6e4838babbdc1"), "x" : 3 }

> db.things.find({name:/ng/}) // 相當於SQL的 SELECT * from things WHERE name LIKE '%ng%';
{ "_id" : ObjectId("......略......"), "name" : "mongo" }

> db.things.find({name:/^mon/})   // 相當於SQL的 SELECT * from things WHERE name LIKE 'mon%';
{ "_id" : ObjectId("......略......"), "name" : "mongo" }

> db.things.find({x:4,j:10}) // 相當於SQL的 SELECT * from things WHERE x=4 AND j=10;
{ "_id" : ObjectId("......略......"), "x" : 4, "j" : 10 }

  // 下行效果同 db.things.count() // 相當於SQL的   SELECT COUNT(*) FROM things;
> db.things.find().itcount()    // 傳回collection things中 documents 個數 (此處22個資料物件,故顯示22)
22

> db.things.find({j:{$exists:true}}).count() // 相當於SQL的 SELECT COUNT(j) FROM things;  即有field j者方計數
20

> var tstcursor=db.things.find() // 重新定義tstcursor於things開頭

> printjson(tstcursor[0]) // 由此可知cursor可如同陣列般操作,陣列開頭註標為0   <註四>
{ "_id" : ObjectId("......略......"), "name" : "mongo" }

> printjson(tstcursor[21]) // 由於有22個資料物件,故 tstcursor[21] 為最後一個
{ "_id" : ObjectId("......略......"), "x" : 4, "j" : 20 }

> printjson(tstcursor[22]) // 最後一個之後的皆視為未定義
undefined

> db.things.find({name:"mongo"}).forEach(printjson)       // 相當於SQL的  SELECT * from things WHERE name="mongo";
{ "_id" : ObjectId("......略......"), "name" : "mongo" }

> db.things.find({x:4}).forEach(printjson) // 相當於SQL的  SELECT * from things WHERE x=4;
{ "_id" : ObjectId("......略......"), "x" : 4, "j" : 1 }
......
{ "_id" : ObjectId("......略......"), "x" : 4, "j" : 20 }

  // 下行的true換成1亦可 i.e. 下行 <=> db.things.find({x:4},{j:1}).forEach(printjson)
> db.things.find({x:4},{j:true}).forEach(printjson) // 相當於SQL的  SELECT j from things WHERE x=4; 
{ "_id" : ObjectId("......略......"), "j" : 1 }
......
{ "_id" : ObjectId("......略......"), "j" : 20 }

> db.things.findOne({x:4})  // <=> db.things.find({x:4}).limit(1) 而SQL為 SELECT * from things WHERE x=4 LIMIT 1;
{ "_id" : ObjectId("......略......"), "x" : 4, "j" : 1 }

> var tstdoc=db.things.findOne({x:4})   // 與上類似,然將變數tstdoc定義成db...findOne()傳回的document   <註五>
> tstdoc
{ "_id" : ObjectId("......略......"), "x" : 4, "j" : 1 }

> db.things.find({x:4}).limit(3) // 由傳回結果易知,當limit()內參數>1 時傳回的不再是document而是cursor <註六>
{ "_id" : ObjectId("......略......"), "x" : 4, "j" : 1 }
{ "_id" : ObjectId("......略......"), "x" : 4, "j" : 2 }
{ "_id" : ObjectId("......略......"), "x" : 4, "j" : 3 }

> db.things.update({x:3},{$set:{x:1}})      // 相當於SQL的 UPDATE things SET x=1 WHERE x=3;  (若有相同亦只改最前頭)
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })

> db.things.find({x:{$gte:1,$lte:3}}) // 由此顯示結果可知,x=3的資料物件 成功改成x=1
{ "_id" : ObjectId("556b87c341e6e4838babbdc1"), "x" : 1 }

> var dmongo=db.things.findOne({name:"mongo"})
> dmongo // <=> print(tojson(dmongo))
{ "_id" : ObjectId("556ae901b81b03c8400c2c04"), "name" : "mongo" }

> dmongo.type="database" // 增一field於 資料物件dmongo中 (其field名為type, field值為database)
database

> dmongo
{
        "_id" : ObjectId("556ae901b81b03c8400c2c04"),
        "name" : "mongo",
        "type" : "database"
}

> db.things.findOne({name:"mongo"}) // 因尙未save(), 故db未更動
{ "_id" : ObjectId("556ae901b81b03c8400c2c04"), "name" : "mongo" }

> db.things.save(dmongo) // 以.save()來更新document資料 (注意若dmongo的_id於things中找不到,則是增添新document)
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })

> db.things.findOne({name:"mongo"}) // 由結果亦知:因collection things中能找到與dmongo相同的_id, 故是更新document
{
        "_id" : ObjectId("556ae901b81b03c8400c2c04"),
        "name" : "mongo",
        "type" : "database"
}

  // 嵌入文件 (已設 資料物件dmongo開始如上定義, 且collection things中已有相同document)
> dmongo.newtstd={a:1,b:2} // 對 資料物件dmongo 再增一field ,此一新field名newtstd, 其值{a:1,b:2}又為一document 
{ "a" : 1, "b" : 2 }

> db.things.save(dmongo) // 以dmongo來更新 資料庫mydb中things裡的document
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })

> db.things.findOne({name:"mongo"}) // 由查詢顯示可知: 子文件 {a:1,b:2}  被嵌入至 父文件 中
{
        "_id" : ObjectId("556ae901b81b03c8400c2c04"),
        "name" : "mongo",
        "type" : "database",
        "newtstd" : {
                "a" : 1,
                "b" : 2
        }
}

> db.things.findOne({"newtstd.b":2}) // 內嵌文件 之查詢例
{
        "_id" : ObjectId("556ae901b81b03c8400c2c04"),
        "name" : "mongo",
        "type" : "database",
        "newtstd" : {
                "a" : 1,
                "b" : 2
        }
}

                        // 參考(reference) 例 - 設操作延續上面
> var other={s:"other thing",n:1} // 建一資料物件other, 其有s,n兩fields
> db.otherthings.save(other) // 自動建立collection otherthings, 並將上行document增添其中
WriteResult({ "nInserted" : 1 })

> db.otherthings.find()
{ "_id" : ObjectId("556d98fe41e6e4838babbdd6"), "s" : "other thing", "n" : 1 }

> var mongo=db.things.findOne({name:"mongo"})
> mongo
{
        "_id" : ObjectId("556ae901b81b03c8400c2c04"),
        "name" : "mongo",
        "type" : "database",
        "newtstd" : {
                "a" : 1,
                "b" : 2
        }
}

   // 下下行於資料物件mongo增一field otherdata,其值為一reference, 此reference用來參考otherthings中的某特定document
   // 注意上面.save(other)已定other._id為ObjectId("556d98fe41e6e4838babbdd6"), 故reference知道要參考哪個document
> mongo.otherdata=new DBRef('otherthings',other._id)     
DBRef("otherthings", ObjectId("556d98fe41e6e4838babbdd6"))
> mongo
{
        "_id" : ObjectId("556ae901b81b03c8400c2c04"),
        "name" : "mongo",
        "type" : "database",
        "newtstd" : {
                "a" : 1,
                "b" : 2
        },
        "otherdata" : DBRef("otherthings", ObjectId("556d98fe41e6e4838babbdd6"))
}

> db.things.save(mongo) // 使 資料庫內的docuement 亦更新
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })

   // 由下行可知,我們已可經由 things中某一特定document的field otherdata  參考到 otherthings中某一特定document
> db.things.findOne({name:"mongo"}).otherdata.fetch()
{
        "_id" : ObjectId("556d98fe41e6e4838babbdd6"),
        "s" : "other thing",
        "n" : 1
}

> other.n=3
3

> db.otherthings.save(other) // 更新collection otherthings中此一特定document
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })

> db.otherthings.find()
{ "_id" : ObjectId("556d98fe41e6e4838babbdd6"), "s" : "other thing", "n" : 3 }

> db.things.findOne({name:"mongo"}).otherdata.fetch() // 從 things 中亦可得知 otherthings 中的更新
{
        "_id" : ObjectId("556d98fe41e6e4838babbdd6"),
        "s" : "other thing",
        "n" : 3
}

> db.things.getIndexes() // 查看於collection things已建之索引  (此為系統為field _id自動建的遞增索引)
[
        {
                "v" : 1,
                "key" : {
                        "_id" : 1
                },
                "name" : "_id_",
                "ns" : "mydb.things"
        }
]

> db.otherthings.getIndexes() // 查看於collection otherthings已建之索引  (此亦為系統為field _id自動建的)
[
        {
                "v" : 1,
                "key" : {
                        "_id" : 1
                },
                "name" : "_id_",
                "ns" : "mydb.otherthings"
        }
]

> db.system.indexes.find() // 此可列出當前 資料庫於各collection所建的全部部索引
{ "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "mydb.things" }
{ "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "mydb.otherthings" }

// 下行相當於SQL的 CREATE INDEX <自設索引名> ON things(j DESC)
> db.things.ensureIndex({j:-1}) // 對 things中的field j建立遞減索引 (-1表遞減; 1表遞增)   <註七>
{
        "createdCollectionAutomatically" : false,
        "numIndexesBefore" : 1,
        "numIndexesAfter" : 2,
        "ok" : 1
}

> db.system.indexes.find()
{ "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "mydb.things" }
{ "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "mydb.otherthings" }
{ "v" : 1, "key" : { "j" : -1 }, "name" : "j_-1", "ns" : "mydb.things" }

> db.things.find({j:{$gt:15}}).count() // 相當於SQL的 SELECT COUNT(*) FROM things WHERE j>15;  理論上index後會較快
5

> db.things.find().sort({j:-1}) // 以field j遞減方式顯示結果,相當於SQL的 SELECT * FROM things ORDER BY j DESC;
{ "_id" : ObjectId("556ba28e41e6e4838babbdd5"), "x" : 4, "j" : 20 }
......
{ "_id" : ObjectId("556ba28d41e6e4838babbdc2"), "x" : 4, "j" : 1 }
......

> db.things.find().sort({j:1})   //  以field j 遞增方式顯示結果; 因是建立遞減索引, 故此應比上面慢 (此例不明顯,須很多data方能看出)
......
{ "_id" : ObjectId("556ba28d41e6e4838babbdc2"), "x" : 4, "j" : 1 }
......
{ "_id" : ObjectId("556ba28e41e6e4838babbdd5"), "x" : 4, "j" : 20 }

> db.things.find({j:{$gte:11}},{j:1}).sort({j:-1})  // 相當於SQL的 SELECT j FROM things WHERE j>=11 ORDER BY j DESC;
{ "_id" : ObjectId("556ba28e41e6e4838babbdd5"), "j" : 20 }
......
{ "_id" : ObjectId("556ba28d41e6e4838babbdcc"), "j" : 11 }

> db.things.find({j:{$lte:7}},{j:1}).sort({j:-1})   // 相當於SQL的 SELECT j FROM things WHERE j<=7 ORDER BY j DESC;
{ "_id" : ObjectId("556ba28d41e6e4838babbdc8"), "j" : 7 }
......
{ "_id" : ObjectId("556ba28d41e6e4838babbdc2"), "j" : 1 }

> db.things.find({j:{$gt:7,$lt:11}},{j:1}).sort({j:-1})  // SELECT j FROM things WHERE j>7 AND j<11 ORDER BY j DESC;
{ "_id" : ObjectId("556ba28d41e6e4838babbdcb"), "j" : 10 }
{ "_id" : ObjectId("556ba28d41e6e4838babbdca"), "j" : 9 }
{ "_id" : ObjectId("556ba28d41e6e4838babbdc9"), "j" : 8 }

> db.things.ensureIndex({"newtstd.b":1})  // 以newtstd.b為key,對things.newtstd內嵌文件建立遞增索引
{
        "createdCollectionAutomatically" : false,
        "numIndexesBefore" : 2,
        "numIndexesAfter" : 3,
        "ok" : 1
}

> db.system.indexes.find()
{ "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "mydb.things" }
{ "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "mydb.otherthings" }
{ "v" : 1, "key" : { "j" : -1 }, "name" : "j_-1", "ns" : "mydb.things" }
{ "v" : 1, "key" : { "newtstd.b" : 1 }, "name" : "newtstd.b_1", "ns" : "mydb.things" }

  // 下行所建索引以field j作遞增排序,當j相同情況下, 按照name作遞減排序, 若前二field皆相同, 以 newtsd.b 作遞減排序
> db.things.ensureIndex({j:1, name:-1, "newtstd.b":1}) // 多columns又含內嵌文件的索引例
{
        "createdCollectionAutomatically" : false,
        "numIndexesBefore" : 3,
        "numIndexesAfter" : 4,
        "ok" : 1
}

> db.system.indexes.find()
...
{ "v" : 1, "key" : { "newtstd.b" : 1 }, "name" : "newtstd.b_1", "ns" : "mydb.things" }
{ "v" : 1, "key" : { "j" : 1, "name" : 1, "newtstd.b" : -1 }, "name" : "j_1_name_1_newtstd.b_-1", "ns" : "mydb.things" }

> db.things.update({x:4},{$inc:{j:10}},false,true) // 相當於SQL的 UPDATE things SET j=j+10 WHERE x=4;
WriteResult({ "nMatched" : 20, "nUpserted" : 0, "nModified" : 20 })

> db.things.reIndex()   // 重建索引

> db.things.find({x:4},{j:1}).sort({j:-1})
{ "_id" : ObjectId("556ba28e41e6e4838babbdd5"), "j" : 30 }
......
{ "_id" : ObjectId("556ba28d41e6e4838babbdc2"), "j" : 11 }

> db.things.dropIndex({j:-1})     // 刪除一特定索引,field(s)須全符合方刪除其索引
{ ... , "ok" : 1 }

> db.system.indexes.find() // 由結果知刪除成功
{ "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "mydb.things" }
{ "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "mydb.otherthings" }
{ "v" : 1, "key" : { "newtstd.b" : 1 }, "name" : "newtstd.b_1", "ns" : "mydb.things" }
......

// 下行以runCommand模式刪除索引:效果相當於 db.things.dropIndex({"newtstd.b":1})
> db.runCommand({dropIndexes:'things', index: {"newtstd.b":1}})
{ ... , "ok" : 1 }

> db.system.indexes.find()      // 由結果知刪除成功
{ "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "mydb.things" }
{ "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "mydb.otherthings" }
......

> db.things.dropIndexes() // 除預設的 _id 索引外,其餘的collection things中索引全都刪除
{
        "nIndexesWas" : 2,
        "msg" : "non-_id indexes dropped for collection",
        "ok" : 1
}

> db.system.indexes.find()
{ "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "mydb.things" }
{ "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "mydb.otherthings" }

> db.things.reIndex()   // 重建索引, 注意!! 重建"不能"復原刪除!只是把既有index重建索引, 故.indexes.find() 結果同上


// map/reduce: 注意map輸出與reduce的輸入 兩者的資料型態須相等
// added in 2016/3/12
I.法
db.things.mapReduce(
        function() {
                var x = this.x;
                emit(x, this.j);
        } ,
             function (key, vals) {
                 var total = 0;
                 for (var i = 0; i < vals.length; i++) {
                     total += vals[i];
                 }
                 return total;
             },  { out : { inline : 1 }  } )

輸出如下:
{
        "results" : [
                { // { "_id" : ObjectId(...), "z" : 15 } and the last document
                        "_id" : null,
                        "value" : 0
                },
                { // { "_id" : ObjectId(...), "x" : 1 }
                        "_id" : 1,
                        "value" : null
                },
                { // { "_id" : ObjectId(...), "j" : 11~30, "x" : 4 }
                        "_id" : 4,
                        "value" : 410 // 11+12+...+30 = 410
                }
        ],
        "timeMillis" : 2,
        "counts" : {
                "input" : 23,
                "emit" : 23,
                "reduce" : 2,
                "output" : 3
        },
        "ok" : 1,
}


II.法
>
db.things.mapReduce(
                function() {
                   var x = this.x;
                   emit(x, {count: 1, j: this.j} );
                } ,
                  function(key, values) {
                        var result = {count: 0, j: 0};

                        values.forEach(function(value) {
                                result.count += value.count;
                                result.j += value.j;
                                } );

                        return result;
                   }, { out : { inline : 1 }  } )

輸出如下:    // 結果類似I.法, 唯此處加了計數器
{
        "results" : [
                {
                        "_id" : null,
                        "value" : {
                                "count" : 2,
                                "j" : 0
                        }
                },
                {
                        "_id" : 1,
                        "value" : {
                                "count" : 1,
                                "j" : null
                        }
                },
                {
                        "_id" : 4,
                        "value" : {
                                "count" : 20,
                                "j" : 410
                        }
                }
        ],
        "timeMillis" : 3,
        "counts" : {
                "input" : 23,
                "emit" : 23,
                "reduce" : 2,
                "output" : 3
        },
        "ok" : 1,
}



<註一>
a.
一開始啟動server發現有問題無法啟動, 以 systemctl -l status mongod.service 查詢:
情況<i> 當含有如下錯誤訊息:
...warning... servers don't have journaling enabled by default. Please use --journal if you want durability.
......
...mongod... ERROR: child process failed, exited with error number 1
經查發現自己的 mongodb-server 的設定檔/etc/mongod.conf內的log設定與其所預建的log目錄不符:
/etc/mongod.conf 中預設是 logpath=/var/log/mongodb/mongod.log
然urpmi mongodb-server 安裝時是建 /var/log/mongo/mongod.log
故產生錯誤
自行解決方式:
經查google後發現置於 /var/log/mongodb/mongod.log 的數量遠大於 /var/log/mongo/mongod.log
本想改以 /var/log/mongodb/mongod.log 為主
然為求未來rpm update 的方便,還是改/etc/mongod.conf 設定成 logpath=/var/log/mongo/mongod.log
然未來新版本若更動亦不排除再改設定

情況<ii>
然若 systemctl -l status mongod.service 查詢出現
...
...ERROR: child process failed, exited with error number 100
...
則查看看/var/lib/mongo/ 目錄中是否有mongod.lock
若server没啟動而此竟然有此當存在, 則表其為先前的程序,
很可能是先前有不正常結束的情況(e.g.當機)而未淸,
此時刪此mongod.lock後通常再執行systemctl start mongod.service即可
若mongodb內先前已存有data, 則一進入後先執行db.repairDatabase()來修復會較保險


b.更新時有問題,出現
ERROR: 'script' failed for mongodb-server-3.0.4-1-omv2014.0.i586: 
error: %post(mongodb-server-3.0.4-1.i586) 短命令稿執行失敗,離開狀態 1
亦不能啟動server
為排除變因,先 
]# mv /var/lib/mongo /var/lib/mongo.old
]# mkdir /var/lib/mongo
]# chown mongod.mongod /var/lib/mongo
其後用 urpmi --replacefiles --replacepkgs mongodb-server 再重裝, 雖安裝没再出現錯誤,然執行仍core dump, 
或許從2.x更新至新版3.0.4有啥問題... (己没找到解決方式,故改回用2.x)


<註二>
傳回結果開頭總會是field "_id"
ObjectId未定義時為系統自動分配,依情況而不同,此處從略
由 db.things.find() 顯示查詢結果亦知,資料庫是 以_id為名的field 來存ObjectId(), 其亦具SQL的primary key性質
由help的db...find() 說明 list objects in collection 可知
我們可視db....find() 所顯示的 一{...} 即為一資料物件,此物件以 "_id":ObjectId("......略......") 屬性作為開頭
而於MongoDB中,此一資料物件即為一document
雖單用db.things.find()所顯示的是查詢結果,然實際上是一object cursor(DBCursor), 故之後有例將變數定義成此cursor
例中剛開始定義j等資料物件{...}最初可視為尙無field _id的document(s), 當第一次.save(j)後才有_id
field _id 的亦有SQL中 primary key 的效果
但若是用db.thing.insert(j) 則直接打j時{...}將無field _id, 而db.things.find()仍有此物件_id就是
   i.e. 兩方法僅使local變數相異, 內部儲存資料物件兩方法皆必有物件_id就是
注意!雖然多數可以對應SQL, 然而多數情況應最好同時將{...}視為(資料)物件,較符合物件導向精神


<註三>
利用while loop與object cursor顯示things中所有資料 (i.e. iterator方式)
tstcursor.hasNext() 與 tstcursor.next() 為Cursor methods
方法printjson()以JSON形式顯示資料
  <自>我們可視object cursor本身即為一物件,其內部有一指標成員(pointer)最初指向 collection第一個待顯示資料物件 的前一位,
      方法hasNext()是讀此指標成員來判斷cursor下一位是否有資料物件; 
      方法next()是 先顯示cursor下一位的資料物件後再將cursor內部的指標成員移至此資料物件

<註四>
注意當cursor一用陣列來操作時,
cursor將停止如<註三>般的iterator方式 (<註三>的<自>中所說的內部指標,相當於指向 cursor[0]的前一位)
且cursor會把註標所指的資料物件皆載入記憶體,若查詢結果較大,有可能因此耗盡記憶體而發生記憶體溢位
故應依自身情況選擇: 當cursor用iterator方式時,可隨時改用陣列方式,而cursor[0]會成為改用當時尙未顯示的第一個資料物件
                    然若是已用陣列操作時想換用iterator方式, 只能重新定義cursor

<註五>
注意! 傳回結果實際上是與 database資料 隔離的新資料物件, 而tstdoc指向此新物件
故未經諸如.save()存入/更新時, 改此物件是不會更動到database的

<註六>
注意用limit()限制查詢結果個數可減輕資料庫與網路的負荷,因而增加效率,改善效能

<註七>
注意索引的遞增遞減是指索引建存遞增遞減,與用.find()所顯示的不見得同向,
顯示是用諸如.find().sort()來控制顯示時呈現出來的遞增遞減


ref.0  MongoDB Manual  -  http://www.mongodb.org/display/DOCS/Manual
ref.1  man mongo
ref.2  挑戰大數據 第二版 - http://datasearch.ruc.edu.cn/NoSQL/
       書中p.11-52 表11-10 為 SQL與MongoDB查詢敍述比較
       此書可能是人民大學的學生上Big Data相關課程的内容彙整,
       也許因課程繁重而筆記匆匆, 以致書印錯誤一些些
       且又没索引; 某部份中文用詞若能保留英文應該會比較好,
       例如常用的'行'與'列'恰與己所學相反
       以前所學 column譯為行 而 row譯為列 - i.e.直行橫列
       然書中把 column譯為列 而 row譯為行
       再例如程設裡 指標一般是pointer,而書中把資料庫的cursor翻成指標, 
       之前没碰過資料庫程設的讀者有可能會在這個地方產生混淆
       若能自行校對,那麼此本仍是很不錯的中文雲端參考書 (注意"能校對"前提!)

2015年12月25日 星期五

模糊關係矩陣的合成 in scilab

// 模糊關係矩陣的合成 in scilab
// 用法例: fuzzycompo(R,S)   => 此函式輸出的模糊關係矩陣 是R,S兩模糊關係矩陣的合成

function C=fuzzycompo(A, B)
  [nrA ncA] = size(A)
  [nrB ncB] = size(B)
  // ncA與nrB須相等, 若想檢查則加scripts於此處
  C=zeros(nrA,ncB);
  for i=1:nrA
    for j=1:ncB
      [minV] = min(A(i,:), B(:,j)');
      [C(i,j)] = max(minV);
    end
  end
endfunction

// 網上或有現成, 但找一會兒没找到就自己寫了

2015年12月11日 星期五

未來

昨與母親大人聊天, 她聽人說未來北京會變成沙漠。
我拿荷蘭與地球暖化例子反駁這個可能:
"人類欲望無止盡, 除非有科技替代, 否則減緩暖化應不可能,
 但像新聞說的那樣預言一堆城市百年後會變成海我也不相信,
 未來應該是海平面愈高, 防洪堤愈高"
凌晨看大愛二台的地球證詞重播正巧講防洪

從節目中看到荷籣電腦控制的防洪堤與抽水機,
我想未來應該會有某種柰米塗料漆在防洪堤靠陸地那面,
利用毛細管原理將接觸防洪堤的水往上引, 防洪堤上端像外面傾斜,
故能引導上端的水會再因地心引力傾流入海
雖只能部份替代抽水,
即使這様荷蘭抽水機的幾十年節省電費也會滿可觀

再看到荷蘭的水陸兩棲屋, 屋背後有中軸, 屋隨水高低而上下
但因電線長度等限制只能提高5公尺,
我想未來可能將中軸作的很高很高,
最上端會有電源收發器, 以微波或雷射來傳送能源,
(能精確判斷傳送位置, 通訊是類似p2p或蜂巢或更先進的傳送)
兩棲屋中有充電電池, 類似現在的充電汽車,
屋中能源皆用自電池, 屋自有檢測電源儲備量裝置,
當覺得想要有充電時, 兩棲屋會自動發送訊息給電廠中的電腦,
電廠再以微波或電射方式傳送能源至兩棲屋

未來即使像凱文科斯納的水世界那樣,
電廠也可設在高山上, 例如喜瑪拉雅山,
太陽能電池放在人造衛星或雲端上的無人飛行器上,
以微波或雷射來傳送強大能源給山上電廠, 山上電廠再分送給兩棲屋
甚或有兩棲屋建造船, 以3D列印方式建造兩棲屋
所以即使最悲觀的水世界, 亦不至像電影那様, 而仍會是個文明世界

過去超級火山爆發而史前人類都還有兩三千人能存活下來,
現代人的手段很多, 不可能不如原始人
地球外的不論, 地球內的東東我想只有 地球人 才能滅絕地球人

2015年7月30日 星期四

stagefright 號稱95%的Android手機都有的漏洞


stagefright-exploit
號稱95%的Android手機都有的漏洞
真正根本補洞的方式是要更新韌體
若要DIY則須從廠商處得到最新韌體(先確定廠商有提供)
待root機過後再來更新韌體...粉麻煩...
(注意!!! 自行更新韌體有死機風險!)
root機後亦最好解除root, 因root機面對此漏洞的風險更大 <ref.2>

漏洞發現者Joshua Drake將於2015/8/5美國黑帽大會上詳細講解此漏洞


中文:
ref.1 https://zh.wikipedia.org/wiki/Stagefright%E6%BC%8F%E6%B4%9E
ref.2 http://tech.qq.com/a/20150727/055160.htm


底下是原文

簡易保護方式:(其實漏洞仍在, 只是不要去觸發它...)
ref.3 http://www.cbsnews.com/news/android-stagefright-flaw-protect-android-phone/

技術相關參考:
ref.4 https://www.twilio.com/blog/2015/07/how-to-protect-your-android-device-from-stagefright-exploit.html
ref.5 http://www.dshield.org/diary.html?date=2015-07-28
ref.6 http://www.androidcentral.com/stagefright-exploit-what-you-need-know

相關新聞串:
https://news.google.com/news/story?ncl=dwQAYkiOH634R6MAB-J4wtJAj2ACM&q=vulnerabilities&lr=Chinese&hl=en&sa=X&ved=0CCgQqgIwA2oVChMIgJyu6oyCxwIVp9mmCh1O4QkR

2015年7月28日 星期二

周星星的超能衣

周爺電影中毒太深, 底下是今早作的夢:


將起黎明尙未至, 陰風陣陣, 達聞西-凌凌漆的發明博士 發明了"周星星超能衣", 發給後方將赴戰場的超能部隊裡每人一件

"真是太神了" 周星星拿起超能衣說道

"上了戰場才能穿" 發明博士達聞西廣播提醒

"那還等什麼" 超能部隊裡每人都等不及地將它穿起, 穿後跳飛 "衝啊"

"不行啊! 環保衣太陽能發電! 現在還没太陽, 裡面只有幾秒能源"  發明博士趕緊說

"什麼" 已跳飛的周星星往下回望

"不用怕, 我們用特異功能支援" 食神火雞姊帶頭衝的先鋒部隊成員齊喊, 紛紛用起移動相關的超能力

不用則已, 没想到, 一用反而更亂

JUMP 瞬間移動者載不動那麼多人的能量, 呈現不規則的分身現象, 使得一些部隊成員開始被撞飛

現場開始出現龍捲風, 一些人被捲進風內

"啊! 上了科學小飛俠的當啦!" 原來先鋒部隊中有成員是科學小飛俠的徒弟

"小日本果然不可信" 旁白説道


然後我就醒了

2015年4月28日 星期二

Ruby , a pure OO language, only supports single inheritance

            此note最初是於寛螢幕下作出的,故宜貼至類似環境下再閱讀較佳

Ruby, a pure OO language, only supports single inheritance.  -- 較適合已有基礎的程設愛好者

執行方式有以下幾種:
I.交談模式 (有些版本不支援此)
ex. hello例:
1. 打ruby進入
2. 打 puts "Hello, world!"           # 註:puts 可簡化成 p
3. 按^D

II.ruby <script檔名>
ex. ruby hello.rb

III. use the Unix ``shebang'' notation as the first line (記得檔名加x屬性)
第一行常用有:
如:#!/usr/bin/ruby -w
或如:#!/usr/bin/env ruby  用此行可避免像上行在不同系統而可能需改path等情形


常量亦為物件,故有其method可用,如:
"gin joint".length  為 9
"Rick".index("c")   為 2
-1942.abs           為 1942
若類別或模組內含constant, 平時可用<類別名或模組名>::<CONST名>來讀取
(注意!constant名的開頭字元必須大寫! 例如class名 屬constant, 故第一字元須大寫)

像諸如 number = number.abs 對ruby是合法的,故使用時須小心

true or false:
只有false和nil表示false, 其他皆表為true, 故在Ruby中,0亦為true.

字串例:
str = "Tom" + " Huse"           # -> "Tom Huse"
str[-4,1] + str[1,2] + str[-1,1]  # -> "Home"

Control Structures:
如 if ...elsif....else....end
如 while......end
當if...end 中間僅一行時用  ... if ... 亦可(後不含end)
同理 while...end僅一行時可用 ... while ...

Array (containers之一) 建立方式例:
I 法ex. empty1 = []
II法ex. empty2 = Array.new
III法ex.empty3 = Array.new(0) # 0表長度為0, 故empty3亦為一empty array

當puts 一array時,自動地一元素列一行; 若需陣列印於一行,則陣列後須加.inspect
ex.
a = [5, 6, 7, 8, 9]
puts a.inspect             # 印出 [5, 6, 7, 8, 9]

for i in 0...a.length      # 因a只五個元素,故此例a.length為5,注意此行三點... 會不含上限,故i只執行到4 (i.e.5-1);若是二點..才會包含上限至i=5
  puts a[i]                # 陣列a元素一行一個依次印出
end

a.length.times do |i|      # 此三行與上for迴圈效果相同 (執行5 次迴圈,預設從0開始,故只執行到4)
  puts a[i]
end


Regular Expressions:  ( =~ 用法同 Perl )
ex1. /P(erl|ython)/ 相當於 /Perl|Python/
ex2. /\d\d:\d\d:\d\d/     # a time such as 12:34:56
ex3. /Perl\s+Python/      # Perl, one or more spaces, then Python
ex4. /^Ruby (Perl|Python)/ # 開頭依次為為 Ruby, a space, and either Perl or Python
ex5. if line =~ /Perl|Python/
        line.sub(/Perl/, 'Ruby')    # replace first 'Perl' with 'Ruby'
        line.gsub(/Python/, 'Ruby') # replace every 'Python' with 'Ruby'
     end



定義方法:(當定義時,方法內引數若有設值則其值為default)
ex1. 底下 def ... end 之間亦可簡化成一行: return "Test, aug: #{m}"  或 "Test, aug: #{m}"  (因 Ruby可省略 return)
def testFn(m='d-0')
  result = "Test, aug: " + m         # <=> result = "Test, aug: #{m}"
  return result                      # <=> result  (因return 可省略)
end
以下為使用上面testFn方法之同例等價敘述:(皆印出Test, aug: a-1 若不加後面引數則預設'd-0', 那麼將為Test, aug: d-0)
puts testFn "a-1"
puts testFn("a-1")
puts(testFn "a-1")
puts(testFn("a-1"))
由上可知呼叫方法時常可省略括號()

上例之result為一區域變數(變數名開頭亦可底線_)

ex2.
def tstr(_arg1, *rest)     # 除第一引數外,其餘引數在array中,此例將這array命名為rest
  if rest == []            # 判斷rest是否為空
    _arg1   # 當呼叫方法時,只給一引數時的情況會執行至此,此例只是將第一引數return
  else
    "#{rest.join(', ')} and #{_arg1}"      # 多引數時,rest不為空,會執行此行,陣列rest之元素間會以', '來合併, 其後return此合併字串
  end
end

puts tstr("me") # 印出 me
puts tstr("me", "alice") # 印出 alice and me
puts tstr("me", "joe", "tom", "bob") # 印出 joe, tom, bob and me
puts tstr(*["me", "joe", "tom", "bob"]) # *[]表呼叫方法時將陣列中各元素展開後傳至各引數, 結果同上行
puts tstr(*(0..9).to_a) # 類似上行,將0至9展開至各引數,故印出 1, 2, 3, 4, 5, 6, 7, 8, 9 and 0
                                # (0..9) 為一Enumerable例,用其方法to_a => [0 1 2 3 4 5 6 7 8 9],故展開需前頭加*
                                # 此例亦可看出 * 的優先權 比 . 低

當變數前加$者,為全域變數 (ex. $CUSTOMER)
當變數前加@者,為Instance Variables (ex. @X, @_, @pt_3), 其通常用於儲存物件 (如後面OOP中之例)
當變數前加@@者,為Class Variables (ex. @@SN, @@tl)

然而當最前頭為大寫字母時,其為類別名,module名,或常數名(<自>實際上三名皆屬constant) (ex. String, MyClass, PI)
    常數於方法內不可指定也不可更動,一般情況下更動會產生警告
    若成常數參考,reference更動性同上,只要指向同一個referent,referent內容可經由其他參考隨意改變 (例如下面ex.2 中之常數參考C)

ex1. called-by-value 與 called-by-reference
# from: <ref.1>
def addOne(n)
  n += 1          
end
a = 1
puts addOne(a)      # -> 2   # called-by-value
puts a              # -> 1

def downer(string)
  string.downcase!  # 原字串string全轉成小寫,傳回值為nil;若不加'!'則傳回轉小寫之字串內容,但對原字串string並未作任何改變
end
a = "HELLO"
puts downer(a)      # -> "hello"  # called-by-reference
puts a              # -> "hello"

ex2. <ref.1>與上例後面同理,變數自動以reference來處理string,故下面不同的變數都參考同一個string
a = b = C = "wxy"  # 注意 C 為常數reference
b.concat("z")      # => a, b, C 印出結果都為 wxyz (因三者都指向同一個referent)
puts a
puts b
puts C
# 上面三行可用 puts a, b, C 取代,結果相同


blocks:
{...} 或 do ... end 間為 code block. code blocks 可用來實作 callbacks

ex. (而yield處如同替換成其{...}之block,且一yield一block) (下行的方法threeTimes為 Iterator, 詳看下段)
def threeTimes # 此例無引數
  yield # 可用yield召喚block
  yield
  yield
end

threeTimes { puts "The block's here" } # block 的括號 { .. } 可用 do .. end 取代


Iterators:
A Ruby iterator is simply a method that can invoke a block of code repeatedly.
An iterator is a method which accepts a block or a Proc object.  (ref.1)
Iterators are used to produce user-defined control structures--especially loops.
註:Ruby的iterator與C++的有些不同,C++的iterator是物件,用來存取或操縱container中元素;而Ruby的iterator是方法,其內部可視以yield來實作(如 threeTimes例中的threeTimes)
    對C++而言,An iterator is an object that "walks through" a collection....(C++ How To Prog. p.568);就應用上,OOP的iterator都有相近之處

<自> 注意! iterator引數 與 block引數 不見得相同 (依己設而定):
           def 方法後若有(),則其內接的是iterator引數(預設其最後為block, 可用block_given?來判斷實際呼叫時是否有給block)
           而 yield後()內接的是block引數

OOP:
ex. (from ref.1)以類別Person之成員age為鍵值,由小到大來排序(設已有一檔名為 ages 之文件檔,每行格式為 人名: 年齡  e.g. bert:  8)
class Person
  attr_accessor :name, :age         # 因底下def <=>...裡只斟酌age, 故此例就key而言實只需 attr_accessor :age 即可
  def initialize(name, age)
    @name = name
    @age  = age.to_i
  end
  def <=>(other)
    @age <=> other.age
  end
end

people = Array.new # 此行使 people 成一空array (值於下面給定) (注意對Ruby而言Array亦視為一物件)

File.foreach("ages") { |l|             # <自> 此處 File.foreach 為 iterator , 其引數"ages"為文件檔檔名;  而 |l| 為block引數
  people << Person.new($1, $2) if l =~ /(.*):\s+(\d+)/
}
# 上面執行完後,陣列people中每一元素皆為一Person物件

pa = people.sort           # pa 為經排序後之新Array
     # 注意! 上行並未改變原陣列people;若是要更動原陣列則將其換成 people.sort!  那麼將無另一新Array, pa就只是people的reference
     # 由於 sort! 須更改原陣列,故在排序完成前people是frozen的;然而此例sort因建新Array而無需frozen。(此分別對後面的threads很重要)

以receiver來呼叫方法(method), receiver可以是物件,類別,或模組。
比如此例的people.sort的 物件people是receiver, 而 sort 為被reciver呼叫的方法 (the method to be invoked)
呼叫方法省略receiver名時,receiver會預設成self,即當前物件(類似C++的this)。


ex. block_given? 例;  <ref.3>
def takeBlock(p1)    # 此例 p1 為 "no block"
  if block_given?    # 偵測呼叫方法時是否有給 block 若有則至下行,否則到else
    yield(p1)        # 最後一行的iterator呼叫方法會執行到此 => s成為p1 亦即 "no block", 故s.sub(...) 使"no "去掉
  else                  # <自> 注意上行相當於 return s.sub(...) 故return "block"  然而s實際上未變更 (類似 無! 的string.downcase)
    p1
  end
end

puts takeBlock("no block")      # iterator引數 與 block引數 相同例;   沒block => 走else => 印出 no block
puts takeBlock("no block") { |s| s.sub(/no /, '') }      # |s|表使s成為block引數


可將block化為 Proc物件,而當於 Proc物件 前頭加'&' 即為block

ex. (例中dbl 與 square 為 Proc物件;&dbl 與 &square 即成 blocks) <ref.1>
dbl = proc { |i| i+i }
square = proc { |i| i*i }

def mth1(&b) #  mth1方法 亦為一iterator (用來invoke block &b)
  print b.call(9), "\n" # 同理,&b為block 而 b為Proc物件; 故此行 以b為reciever 來呼叫方法call
end # &b 為block ,故mth1方法內若用 block_given?判斷 則會為true

mth1(&dbl)              # -> 18      # 不加()亦可,但會有warning訊息
mth1(&square)           # -> 81


def mth2
  print yield(9), "\n"      # yield()作用與方法mth1中的 b.call()的作用相同,由於此處引數亦為9,故底下mth2()結果亦相同
end

mth2(&dbl)
mth2(&square)

puts( (1..10).collect(&square).join(", ") )    # (1..10)為一Enumerable例,用其方法collect(block)可疊代block回傳結果成一array,
                                               # 故後可用方法join()
                                               # 而印出 1, 4, 9, 16, 25, 36, 49, 64, 81, 100  (注意:collect 亦為 iterator)


一些現成的iterators:

ex. 6.upto(9) { |i|  print i }       # 印出6789 (upto() 為iterator)

ex. 3.times { |i| print i }    # 印出012  (i.e. 執行三次block) (times 為iterator)
ex. ('w'..'z').each  { |j|  print j }       # 印出wxyz   # each is an iterator--a method that invokes a block of code repeatedly
ex. ('w'..'z').each  { |j, k|  print j }    # k為欲傳入的第二引數, 由於此例只傳入第一引數j, 故k為 nil => 仍是印出wxyz



ex. <ref.3>
class TaxCalculator
  def initialize(name, &block)
    @name, @blocktst = name, block   # 平行賦值(parallel assignment), 注意此處將 Instance變數@blocktst 指定為一 Proc物件(其物件名為block)
  end
  def getTax(amount,intr) # <自> 此成員方法內之accept同為物件成員的 @blocktst內之Proc物件
    "#@name on #{amount} and interest #{intr} = #{ @blocktst.call(amount,intr) }"
  end # 注意 Proc物件 於其物件建立時即載入而不是經由getTax呼引而來,故用if block_given? 判別會為false
end

tc = TaxCalculator.new("Sales tax") { |amt, i| amt * i }

puts tc.getTax(100, 0.075)         # 印出 Sales tax on 100 and interest 0.075 = 7.5
puts tc.getTax(250, 0.08)          # 印出 Sales tax on 250 and interest 0.08 = 20.0

上例中的getTax為方法,其receiver為物件tc。
省略方法的括號時,有些Ruby文件會稱其為命令(commands),其本質上仍為方法。比如上例第一個puts行亦可寫成 puts tc.getTax 100, 0.075

<ref.6>
     You might be better off thinking of the block and the method as coroutines, which transfer control back and forth between themselves.
     (<自> coroutines 字典中無此字,co- 有共同,協同之意, 此處應是表明應視block與method為相互協同合作之常式)

雖然Ruby號稱 "Everything is an object". 但精確地說只是 "Almost everything is an object in Ruby".
比如上例的方法getTax()不算物件,但Ruby有Method類別可使上例getTex()封裝成 方法物件(be wrapped in an object)。
如上例之後再接一行 mtd = tc.method(:getTax)   那麼 mtd 才是 方法物件(由上例tc的getTax()封裝而成)。此即為objectify(物件化)
ex. 接上例,下面兩行與上例最後一行結果相同
mtd = tc.method(:getTax)
puts mtd.call 250, 0.08

<自> 雖依照Ruby而言,例中 方法getTax 應亦為iterator (其accept同為物件成員的 @blocktst內之Proc物件)
     然依其他OOP(如C++) 而言, mtd 方為iterator [因 方法物件mtd 才是物件, 而就C++等 iterator是物件(或變數)]


ex. <ref.5>
class A
  def a
    p "blah"
  end
end

class Method # <自> 注意Ruby內建Method類別, 故這裡只算加一成員方法aa於一已存在類別Method
  def aa
    p "hi"
  end
end

f = A.new
f.a # 印出 "blah"
f.method(:a).aa # <自> f.method(:a) 為方法物件,屬於Method類別,故能呼叫上面加入的方法aa, 因而印出"hi"


輸入:
gets為標準輸入,且亦會把輸入值存入$_
ARGF為Ruby提供之已存在物件,表input stream
ex. 印出標準輸入中包含"Test"之行
ARGF.each { |line|    print line  if line=~/Test/ }

General delimited input : %q :Single-quoted string;   %Q, %   :Double-quoted string;    %w :Array of tokens   %x :Shell command
                          %r :Regular expression pattern

ex. 利用 %w 建立一陣列後,其元素為字串,
     下下行會輸出元素 |...|間之animal相當於yield之引數,each為iterator
a = %w{ ant bee cat dog elk }      # 使a成Array, 如 a[2] 為cat
a.each { |animal|   puts animal }  # iterate over the contents

ex. (上例全皆印於同一行而以 -- 分隔)
[ 'ant', 'bee', 'cat', 'dog', 'elk' ].each do |animal|
  print animal, " -- "
end


ex.  Fibonacci <ref.3> <ref.4>
def fibUpTo(max)              # 此亦為 iterator 的實作
  i1, i2 = 1, 1
  while i1 <= max
    yield i1                  # 由此處 i1 知 block 有一引數
    i1, i2 = i2, i1+i2        # 注意!當平行賦值(parallel assignment),等號右邊一律為舊值
  end
end

fibUpTo(100) { |f|   print f, " " }     # 印出 1 1 2 3 5 8 13 21 34 55 89
puts

b=[]
fibUpTo(30) { |val|   b << val }
puts b.inspect                        # 印出 [1, 1, 2, 3, 5, 8, 13, 21]

# 底下六行效果相當於上面 b 三行code;  (由此可看出 Proc物件 不只能當引數,亦可當return結果, 此與一般物件相同)
def into(anArray)                  
  return proc { |val|   anArray << val }
end

fibUpTo 30, &into(a = [])      # 注意 1.陣列變數a 在此處首次出現,故不是區域變數  2. into方法 return一Proc物件, 故into前加&,以適用於 fibUpTo
puts a.inspect                 # 結果與b同,印出 [1, 1, 2, 3, 5, 8, 13, 21]


ex. (改自 <ref.4>)
class Song
  def initialize(title, artist, serialnumber)
    @title, @artistname, @sn = title, artist, serialnumber
  end
  def name
    @title
  end
end

class SongList
  def initialize
    @songs = Array.new
  end
  def append(aSong)
    @songs.push(aSong)
    self     # 回傳當前物件指標; i.e. <=> return self  <=> 相當於 C++ 的 return this;
  end
  def deleteFirst
    @songs.shift
  end
  def deleteLast
    @songs.pop
  end
end

slist = SongList.new        # slist 為一 SongList 物件, 此例其亦為一container

slist.
  append(Song.new('title1', 'artist1', 1)).    # 同一物件連續呼叫方法,可用.來依序連用方法 (好處為物件名只需打一次,看起來較簡潔)
  append(Song.new('title3', 'artist3', 3)).    #   <自> 因於def 成員方法 內最後有用self回傳當前物件指標,故可如此作
  append(Song.new('title2', 'artist2', 2)).
  append(Song.new('title4', 'artist4', 4))

puts slist.inspect

puts slist.deleteLast.inspect      # 'title4'... 之 Song 物件 從陣列中刪除,此行亦用 puts 印出這個被刪除的物件

class SongList
  def [](key)                      # 對SongList類別加建一形式為 [key] 之方法
    return @songs[key]  if key.kind_of?(Integer)          # key 為整數時處理,由於此行下面才能用如 slist[1] 來表其第二元素
    return @songs.find { |aSong|   key == aSong.name }      # find 亦為 iterator,由於此行,下面方可用如 slist['title3'] 方式來存取元素
  end
end

puts slist[1].inspect # 印出 'title3'... 這個 Song 物件
p  slist[0].inspect # 印出 'title1'... 這 Song 物件

p  slist['title3'].inspect      # 此處與 puts slist[1].inspect 結果相同


lambda:
用法幾乎與proc同,甚有文件視兩者同義 (<ref.10> lambda .... Synonym for Kernel::proc)
然兩者還是有些許差異: lambda與proc在處理return時有些許不同,詳例可參考<ref.11>
自己習慣上return通常用於method中而 不在 block/proc/lambda 的裡面使用, 以避免混淆

ex.hello例
testh = lambda { puts "hello" }
testh.call
testa = testh
testa.call



Hash: (containers之一)
ex1. 定義hash例
instSection = {
  'cello'     => 'string',
  'clarinet'  => 'woodwind',
  'trumpet'   => 'brass',
  'drum'      => 'percussion',
  'oboe'      => 'woodwind',
}

p  instSection['oboe']      # 印出 woodwind
p  instSection['cello']         # 印出 string
# 上兩行 <=> p  instSection['oboe'];  p  instSection['cello']       # ';'作用類似C, 但於Ruby中非必須

p  instSection.length           # => 5  因上面有5對
p  instSection.inspect          # 印出 {"oboe"=>"woodwind", ... }  i.e.印出不見得會未按照上面宣告次序(依不同Ruby版本而定)
p  instSection          # 由於只顯示一般字元,不加inspect亦可

# 下行増一element (其為Element Assignment---Associates the value given by aValueObject with the key given by aKeyObject. <ref.7>)
instSection['violin'] = 'string'
p  instSection.length           # => 6  因増一elment故成6對
p  instSection # 印出hash亦證明"violin"=>"string" 増入

p  instSection.keys # hash中所有的keys所組成之array, 故顯示 ["cello", "clarinet", "trumpet", "drum", "oboe", "violin"]
p  instSection.values # hash中所有的values所組成之array, 故顯示 ["string", "woodwind", "brass", "percussion", "woodwind", "string"]

# 下行減一element
p  instSection.delete('oboe') # 刪一element, 回傳所刪除之值, 故此行會印出 "woodwind"
p  instSection.length;   p  instSection     # 顯示 長度成5; hash中變成少了 "oboe"=>"woodwind" 元素
p  instSection['oboe']      # 印出 nil  (表無此元素)

p  instSection.sort # 以key排序後的結果以陣列顯示:[["cello", "string"],......, ["trumpet", "brass"], ["violin", "string"]]
p  instSection # 顯示仍為未排序的hash, 表上行的sort並未影響原來的hash
p  instSection.sort {|a,b|  a[1]<=>b[1]}   # 以value排序(hash中元素:[0]-key; [1]-value), 故顯示如下:
              # [["trumpet", "brass"], ["drum", "percussion"], ["cello", "string"], ["violin", "string"], ["clarinet", "woodwind"]
              # 由上故知 instSection.sort {|a,b|  a[0]<=>b[0]} 與不加block的instSection.sort 兩者結果相同

# 下行淸除hash中所有elements
instSection.clear
p  instSection.length;   p  instSection     # 其亦顯示 長度成0; hash成空{}
p  "empty hash"   if instSection.empty?  # 亦可用.empty?來判斷hash是否為空


ex.2 定義一hash, 其名為testh3, 當指定之key未定義時,將傳回預設值9
testh3 = Hash.new(9)            # 9為我們指定的預設值
testh3['testkey1'] = 3
testh3['testkey5'] = 8
p  testh3['testkey5']           # 印出 8
testh3.replace({ "testkey1" => 300, "testkey5" => 700 }) # 以replace來更改hash中已存在元素
p  testh3['testkey5']           # 印出 700

p  testh3 # 顯示 {"testkey1"=>300, "testkey5"=>700}
testh = { 'testk3' => 60, 'testkey5' => 900 }
testh3.update(testh) # 以上行所建立的hash資料來更新testh3
p  testh3 # 顯示 {"testkey1"=>300, "testkey5"=>900, "testk3"=>60}

p  testh3.has_key?('testkey1')  # 可用 .has_key? 來判斷hash中是否有此key, 因上行之故,此會印出 true
p  testh3['tstkey']             # 由於tstkey未定義,故印出9
p  testh3.has_key?('tstkey')    # 雖有預設值,然實未設定,故印出false


# 當方法的引數當中有hash時,允許省略大括號 { }, 故ex22程式中的兩行程式碼:
# testh = { 'testk3' => 60, 'testkey5' => 900 }
# testh3.update(testh)
# 可以用 testh3.update( 'testk3' => 60, 'testkey5' => 900 ) 來替代



fork 與 thread 的不同:
fork是利用OS去建立子程序,而 thread 是由ruby解譯器實作出來的
故fork較慢,且彼此不分享記憶體空間,其優點在於能暫停等待I/O完成再繼續進行,而thread不行
                                   [ thread 亦會分享global, instance, (thread外部已有的)local var. ] ......(**)
當thread遇deadlock時整個程序都將停止;但thread較快又不會導致thrashing <自註>,且因是由解譯器實作,故即使在DOS下亦能用thread
注意: 每一thread在thread's block內定義的local variables範圍只限於thread自身

ex.
tds = []
for name in ['one', 'two' ] do  # 此迴圈建立兩個threads, 分別命名為one與two
#  tds << Thread.new {        # 下行註解觀點來自於  <ref.8> :
#    localName = name         # 在thread裡面用到name的習慣不好,由於(**)之故, 存取於內可能因其他thread使變數意外被更動 故最好改成下行
   tds << Thread.new(name) { |localName|     # 陣列tds中的元素相當於thread物件,thread中內容為此block, 此例結果陣列tds會有二元素
      a = 0
      3.times do |i|
          Thread.pass       # Invokes the thread scheduler to pass execution to another thread.
          a += i
          print localName, ": ", a, "\n"
      end
   }
end
                           
tds.each {|t|    t.join }    # join 方法確保thread完成前不會結束程式

# 印出結果如下: ( 與<ref.1>略有不同,thread特性使然 )
# two: 0 # i 一開始為0, 故 a+=i 後 a仍然為0
# one: 0
# one: 1 # i 為差分;  a 為和分
# one: 3
# two: 1
# two: 3
#
# <ref.1>所顯示的結果:
# onetwo: : 00
#
# onetwo: : 11
#
# onetwo: : 33
# # <自> 此空行亦為thread中之'\n'所造成



一些thread方法:
Thread.current       # 傳回當前正在跑的thread
Thread.list          # 傳回所有的threads之list
(thead個例).status   # 傳回此thread的狀態: sleep或run ; false表此thread已正常結束 ; nil 表此thread已意外結束(terminated with an exception)




<自註> thrashing 於此應可翻為顛簸

ref.
ref.1 file:///usr/share/doc/ruby*/FAQ.html
ref.2 file:///usr/share/doc/ruby*/ProgrammingRuby-0.4/index.html
ref.3 file:///usr/share/doc/ruby*/ProgrammingRuby-0.4/tut_methods.html
ref.4 file:///usr/share/doc/ruby*/ProgrammingRuby-0.4/tut_containers.html
ref.5 http://stackoverflow.com/questions/529315/how-do-i-get-the-method-object-for-a-method
ref.6 file:///usr/share/doc/ruby*/ProgrammingRuby-0.4/intro.html
ref.7 file:///usr/share/doc/ruby*/ProgrammingRuby-0.4/ref_c_hash.html
ref.8 file:///usr/share/doc/ruby*/ProgrammingRuby-0.4/tut_threads.html
ref.9 https://ruby-china.org/topics/19930
ref.10 file:/usr/share/doc/ruby-*/ProgrammingRuby-0.4/builtins.html
ref.11 http://railsfun.tw/t/method-block-yield-proc-lambda/110