英文法の話パート3、which,thatの使い方についてなど
パート1: 英文法の話、時勢と動詞のニュアンスについてなど - 素人がプログラミングを勉強していたブログ
パート2: 英文法の話パート2、to doとsomething to doの見分け方と使い方についてなど - 素人がプログラミングを勉強していたブログ
質問者 The rumor that he got married is false. The rumor that he heard is false. この違い javascripter The rumor of him having got married The rumor which he heard まあこれは明らかに違うな 質問者 前者はThe rumor = got marriedで後者はthe rumor≠he heeard 後者のthe rumorはheardの目的語でrumorを修飾しているから形容詞節で前者は=で結びつける接続詞で同格を表す名詞節 この辺りの見分け方がむずいけどなんとなく分かってきた 質問者 つまり最初に言ったI had a dream that I climbed Mt.Fuji.もa dream=I climbed Mt.Fujiが成立するから名詞の同格になり名詞節でthatはただの接続詞。This is the house where I was born.もthe house ≠ I was born.なので同格ではなく形容詞節か。 質問者 Climbing Mt.Fuji is a dreamでSVCが成り立つってことか。 質問者 つまりClimbing Mt.Fujiとa dreamの関係は=。being bornとthe houseは=で結べない。being born is the houseにはならないつまりSVCの関係ではないってことか javascripter The rumor is false. He got married. Then rumor is false. He heard javascripter 後者は抜き出すと単語が抜けてて文章が未完成になる これが見分け方 質問者 hearの目的語がないとこが見分けるポイント? javascripter そう 質問者 get marriedは自動詞だから独り立ちできる文でSVで完全自動しだけどHe heardはSVOをとるものだからSVにはならないってことか javascripter Whichと同じで The rumor which he heard is falseは He heard the rumor. The rumor is false. みたいに 二文にわかれてるから このタイプは、探すと抜けてる部分があるから 分離して完成しないのが後者 質問者 そっちのほうが分かりやすいかも
英文法の話パート2、to doとsomething to doの見分け方と使い方についてなど
パート1: 英文法の話、時勢と動詞のニュアンスについてなど - 素人がプログラミングを勉強していたブログ
パート3: 英文法の話パート3、which,thatの使い方についてなど - 素人がプログラミングを勉強していたブログ
質問者 I should have taken some medicine to get over a cold. これってto get over a coldが直前のmedicine(名詞)を修飾する形容詞句とも考えられるしtake(動詞)を修飾する副詞句とも考える事ができると思うんだけど、だとしたら文の意味って2つに分かれない? 形容詞句でとらえると(私は風邪を克服するための薬を飲むべきだった) 副詞句でとらえると(私は風邪を克服するために薬をのむべきだった) javascripter 厳密にはそうだが Medicine, to get over a cold の方の解釈が自然 javascripter なぜかというと I should have take some medicine for a cold みたいな言い方があるから I should have が来た時点で、~to doで To doのためにI should haveをするべきだったっていう Because的なものが来ることを期待してるから Toが javascripter I should have got a drink to hydrate myself earlier In order to hydrate myself earlier, I should have taken a drink 俺にはこう分解して聞こえるのが自然な感じがする javascripter 特に To doのあとにmyselfとかがくるも A drink to warm myself up みたいになるから MyselfはIにかかってるから I should have got a drink, to warm myself up、のほうがmyselfが主語からきてることが明らかだから そっちにわけて読みたくなる、 結果的には意味は同じだから 目的がどこにかかってるのかによるねこれは javascripter I wanted some coffee to wake me up I wanted some coffee to wake up wake me upしてくれるコーヒーが欲しかったのが前者 質問者 副詞句って文全体を修飾する場合は目的が文全体にかかるのかね javascripter Coffee が欲しくて、その理由がto wake upなのが後者 In order toだと 目的って意味が強くなる、修飾の意味が減る さらに、明確にするときは Toではなくforとかwhich, whichとかthatとかで修飾 あるいは 動名詞を先にもってきて形容詞にして 質問者 まあそれもありだけど それだとmedicineを修飾することにならない? javascripter I wanted some strong coffee for staying awake さっきいったのはmedicineを修飾する 文全体の場合は Toを最初に持ってきて文法的にはなすか、カンマおくか、in order toとかbecause とかをいれる 質問者 まあそうなるな 質問者 because(副詞節)か前置詞+名詞(副詞句)を入れると文全体、動詞、形容詞が修飾される javascripter I need some coffee. I need to wake up I need some coffee to wake up. I need some coffee as I need to wake up. I need some coffee for waking up. I need some coffee in order to wake up javascripter I need coffee as it wakes me up I need coffee that wakes me up 質問者 I need some coffee for waking upってS V O for O? javascripter For doing = To do 質問者 あぁ javascripter てかどちらでも意味が変わらないからtoで使うんだと思う 質問者 あー現在形だと状態も動作も過去現在未来の習慣だからか javascripter いや、そこらへんはあんまり関係ないかな 何がどこまで修飾するのかってのは英語でも 文脈によることが多くて 質問者 I drink some coffee to wake up とかは javascripter To wake upはI drinkの目的になってる javascripter some coffee to wake upで切り出すと wake up 誰っていう部分がぬけるから javascripter I wake upしたいわけだから to wake up はIのほうにつく javascripter Grab some coffee to wake yourself up! こういう風な言い方の場合 javascripter コーヒーを飲んで目を覚ませ! to wake yourself upは(You should ) grabにかかってるからto wake yourself upがcoffeeにかかる 質問者 Iにかかるとしたら名詞相当語句ってこと? 質問者 つまり不定詞の名詞的用法ってこと? javascripter 用語はわからないけども 主語を常に意識して wake upなんだとしたら 何がなにをwake upしてるのか そう考えるとどこできれるのかだいたいわからないかな javascripter I drink coffee to wake up coffee to wake upだけだと主語が存在しないから成立しないっしょ Coffee to wake you upみたいなかたちにしないといけない javascripter I drink coffee, to wake up.の場合は I drink coffee, in order for me to wake up => I drink coffee, because I want to I wake up 主語が連続してIだからwake upを他動にかえなくてよくなる 質問者 to wake upがdrinkにかかってるんでしょ javascripter I drink, to wake up 短くするとこう dishes to be cleaned dishes (that needs )to be cleaned javascripter some coffee (that makes SOMEONE to wake up) このSOMEONEの不在さが coffee to wake upで区切るときの不自然さ 質問者 I drink cofee to wake up も修飾されるものはcofeeとdrink javascripter コーヒー自体は立ち上がらないわけだから javascripter 背後に主語があるはずなのに、名刺単位でそこで区切られると 主語の情報がきれる javascripter いや、to wake upはI drink coffee全体にかかってる 質問者 私は起きるためにコーヒーを飲む 私は起きるためのコーヒーを飲む 私はコーヒーを飲む起きるために javascripter becauseとかと同じで補足情報だから。 質問者 起きるために私はコーヒーを飲む javascripter I wake up to drink coffee 質問者 それだと名詞的用法になる 質問者 to drink cofeeは目的語 javascripter I drink coffee to wake up I drink coffee to clear my mind I drink coffee to help wake up early javascripter ちょっと質問の内容見失いかけた なにが聞きたいんだっけ 質問者 いや、形容詞句ともとらえられるし副詞句ともとらえられるからそれだと全く違う意味になるんじゃないかって思って javascripter 全く違う意味になることはあるよ ただ I drink coffee to wake upの場合は I drink something, and that something is coffee to wake up に分解するとcoffee to wake up,になって 主語が消えるから分解できるパターンは (I drink )coffee (to wake up) しかありえないんじゃないのっていう 質問者 つまり名詞を修飾するってこと? javascripter toがSVの目的を示してる javascripter 名刺を修飾していないとおもうよ javascripter In order to get ready for work, I drink coffee in the morning to wake (myself) up. javascripter これくらい長くなるとわかるとおもうけど toが名詞を修飾しているというのはワンパターンにすぎなくて 分全体を修飾していることが多い 質問者 She became beautiful to enter a contest. 質問者 うーん javascripter She became beautiful because she wanted to enter a contest. javascripter これもto doが名詞を修飾していないよ 質問者 S V C to doはCとVと文全体かな javascripter to doは分全体とは関係ないから分けて考えたほうがいい javascripter なんか長めの文章ないの 質問者 なんかだして 質問者 He has gone to buy some cigarettes. javascripter I woke up at 7am to go to the gym before classes to make this a habit for my future self to be healthy javascripter I woke upが基本で To go to the gymがその理由 to make it(going to the gym) a habit. がI woke up to...からto go to the gymにかかってて for my future self to be healthyは、to make this a habitにかかってる ほとんどがbecauseで置き換えられるんだって I woke up at 7am to go to the gym before classes, because I want to make a habit of going to the gym, because I want my future self to be healthy. 質問者 (主節)I(代名詞・主語)woke up(自動詞)at7am(副詞句)to go to the gym(副詞句で動詞を修飾している)(従属節)because I(主語・代名詞)want(動詞) to make a habit of going to the gym(不定詞の副詞的用法で動詞を修飾している),(従属節)I(主語・代名詞)want (動詞)(目的語)my future self (不定詞の形容詞的用法で直前の名詞selfを修飾している)to be healthy. javascripter I want to have some medicine for my sleeping issues I want to have some medicine to sleep properly I want to have some sleeping pills I want to have some medicine to fall myself into sleep faster javascripter どこが 薬局の人への説明で どこが薬の説明なのか 意識して日本語に訳してみ これは文脈からしてどれも全く意味がかわらないので 存在しないんだよな明確な訳が javascripter つまり 特定の場合を除いて toを使った結果、目的がどの部分にかかっているのかはwhichと同じで どこに対してでもありうることがあるということで javascripter 文脈からありえない可能性を排除していくしかない、厳密に書きたい場合はToですまさずにFor someone toとかin order for someone toとか, whichやthat、あるいは二文にするなどしてわけないといけない 質問者 for my sleeping issuesって形容詞句と副詞句で分かれるんじゃね。 形容詞句でみるとmedicineを修飾するから(私は不眠の薬を飲みたい)になって副詞句でみると(不眠のため私は薬を飲みたい)よく考えたらin orderも形容詞句の意味を持つから明確な訳がない。明確にするとしたら節で繋ぐのがいいのかね。 おやすみ。 javascripter I want to have some medicine for my sleeping issues javascripter だと Myがあるから 目的はI want to haveにかかってると捉えるのが自然 javascripter なぜかというとmedicine for my ...みたいに自分に限定されたmedicineが欲しいわけではないから javascripter 逆にI want to have some medicine for sleeping issuesだと medicine for sleeping issues とかかっていると捉えるのが自然 javascripter 同様にto sleep properlyもI want to have some medicine全体にかかってる Some sleeping pillsはまあそのままで To fall myself...も文全体にかかってる 質問者 i should have taken 〜 薬を飲むべきだった(飲まなかったので後悔している) 風邪を治すための薬を飲むべきだった(薬は飲んだけれども違う薬を飲んで後悔していると考えられる) ってなんのかな javascripter これは曖昧性があるな、どちらともとれるな。この場合は、文章からは区別できない 発音時に I should had taken some medicine FOR A COLD, not for headaches みたいに 後者の場合にforを強調して発音すると後者に聞こえる 普通は前者 ただし、文章からは区別できない 質問者 ほう
英文法の話、時勢と動詞のニュアンスについてなど
忙しくてブログを全然更新していなかったが、最近、友達に英文法を教えているので、 役に立つかもしれないのでログを載せておく。
パート2: 英文法の話パート2、to doとsomething to doの見分け方と使い方についてなど - 素人がプログラミングを勉強していたブログ
パート3: 英文法の話パート3、which,thatの使い方についてなど - 素人がプログラミングを勉強していたブログ
質問者 Leaves turned red in fall.とLeaves got red in fall.って同じ? javascripter 同じ。Turnedのほうがただ、 変わったという動作が少し強調されてる感じするがほぼ変わらないと思う 質問者 なるほどね javascripter I got 20 years old I turned 20 years old javascripter これだとturnedのほうが 誕生日迎えたっぽさが強い感じするっしょ 質問者 ネイティブはどっちも使うん? javascripter 使う Turnのほうがでもいい 質問者 ネイティブらしく使うには洋書とか読んで覚える? 背景にあるロジックとかニュアンス的な部分 javascripter そもそも TOEIC600くらいはないと 読めないよたぶん javascripter ある程度点数あがってからはじめて色々まあまあ読めるようになって細かいニュアンスの違いがわかってくる 質問者 なるほどね 質問者 Leaves turned red in fall. it suddenly got dark. Leaves got red in fall. It suddenly turned dark. 同じ意味? javascripter 二個目のleaves got red in fallが Leaves turned red in fallより動作の強調具合が弱い以外同じ javascripter Suddenly got Suddenly turned これ入ると同じ javascripter 百円もらった 百円受け取った 意味は同じでも 受け取ったみたいに、意味が限定されてる言葉のほうがちょっとフォーマル寄りで意味が強い感じがするでしょ そういう感じ 質問者 これ動詞の選択問題でどちらもsvcをとりどちらも〜なるって意味だけどどれが適切なのか分からなかったから 質問者 答えではturned red got darkだった 質問者 その程度の違いなのか javascripter その程度の違いだけど Turnedにはもともとsuddenlyのニュアンスが含まれてるから javascripter Gotにsuddenlyをつけるほうが意味がダブらない ただ急にという意味を強く込めてsuddenly turnedという言い方も普通にするから まあ、どういう話なのかによるけどね 質問者 Everyone likes to hear the chimes.って文あってる? 質問者 みんなはそのチャイムの音を聞くのが好きですって訳されてるけど javascripter あってる I'd like to I would like to これはわかるっしの これが現在になっただけ 質問者 好きかどうかはチャイムを聞いてみないと分からんくね javascripter 聞いてるから好きなんだろ 過去から未来全体だから現在で、 I like to do it I like doing it どちらもありうる javascripter To hear the chimes is what everyone likes こういうことだよ 質問者 チャイムを聞くことが未来に向いてるけど、それまでに聞いたことがあって好きだと分かってるなら動名詞じゃないの javascripter I avoid taking to people. 人と話すことを避けます I try to avoid talking to people. 人と話すことを避けようとしてます javascripter 未来に向かっている動作が含まれている場合は 動名詞とは限らない To doも普通にある 質問者 Everyone likes to hear the chimes.ってみんなはチャイムを聞くことが好きって話し手は言ってるけどto doは未来を表していてまだ聞く行為を行っていないのになんで好きって分かるの?って先生に聞いたらそれは間違ってるって言われた javascripter People vary in how they apply these phrases, but I reckon the general pattern is: "I would like to do" is for hypothetical situations. "I like doing" is for describing things that you actually do. There's a slight difference in meaning between "I like to exercise" (I think it's the right thing to do) and "I like exercising" (I enjoy it). That's how I see the difference, but I know some people have other ideas. ネイティブの意見は Like to doもlike doing もどちらも正しい 質問者 おっけ javascripter えっと さっきから言ってるけどto doが未来を表してるから未完了だからダメっっていうのが違くて 好きっていうのは そもそも 未完了で未来ってわけではなく 現在系だから javascripter To do「すること」「が好き」 っていう風に分解されてる 質問者 聞くことが好きって既に聞いたことあるから好きじゃないの javascripter すでに聞いたことがあり、そして 好きという状態が、過去、現在、未来にわたっているから javascripter 全体でみて Likeが現在である限り Doingでもto doでも関係がない そもそも時制をきめてるのはto doかdoingかではなく Likeの部分だから 質問者 likeは状態動詞だね javascripter うん 質問者 like doing = 一般的、習慣的なこと、進行中のこと、体験済みのことなどに、一方 like to do= その場の特定の行為や未体験、未来の行為に用いると説明されて います。 like doingはチャイムを聞いて体験済みで好き状態になってるんじゃないの javascripter うん javascripter いや 質問者 話し手はなんでチャイム未体験なのに好きだと判断したの javascripter そもそも Everyone がto hear the chimes を体験してること自体が不自然なわけで そもそも全員が聞いているということ自体が、強調というか仮定なわけで javascripter Everyone likes to hear the chimes because the chimes are so nice javascripter こういう可能性もあるわけで 質問者 あー 話し手の仮定ってこと? javascripter 仮定ってほど仮定ではなく事実のこともあるけど 質問者 でもそれだったら仮定文になるんじゃないの javascripter 仮定ではなく強調ってことだよ javascripter 俺は絶対に勝つ javascripter これは未来だけど 現在になってて強調されてるでしょ 質問者 まあ 質問者 everyone likes to hear the chimes(みんながそのチャイムを聞けば好きな状態になると確信している)ってこと? javascripter 喋り方にもよるけど javascripter ぶっちゃlike doingと変わらないけど まあ原理的にはそうといえばそう はむずいけどこれ以上説明しようがない 質問者 あーたしかにlikeの説明で理解した 質問者 I go to the gym to do exercise. じゃあこれも現在形で過去現在未来において変わらぬ真理だから文としてはあってる? javascripter I go to the gym (in order )to do the exercise この場合は目的だから、For doing the exerciseでもよくて 質問者 これだと、習慣的な動作どうしだからto doのところはdoingでもいいのか javascripter どちらでもいい 目的を表してる補足説明だから時制はI goの部分で表現されてる 質問者 現在形の動作動詞自体に習慣的な動作の意味が含まれてるから javascripter そう I go to work everyday for my future 質問者 likeも過去現在未来においての状態だからそう考えるとto hearも理解できるな javascripter あくまで、時制は動詞部分にあるから それ以外の部分は明確には時制は示されていない To doが未来っぽいのもあくまで傾向なだけで 基本の時制は全て動詞にある For my futureみたいに、途中で別の未来を表したいなら、そこで書く To doが未来なのは I go to the gymみたいに、 Toがもともと目的地みたいな、先のものを示すからで 逆にdoing は現在している動作だから すでに行われている=過去にはじめて現在の動作 というふうに そこからニュアンスの違いが生じてる 質問者 なるほど javascripter まあそんな感じ 質問者 To自体に目的の要素が含まれてることは知ってたけどto doの方ばかりに目がいってたわ javascripter 言葉ってのはそもそも 辿っていくとシンプルなもとになってる発想と使い方があって いろんなことを表現するために、そこから派生してるから javascripter よーく考えると背後にはまあまあロジックがある
一つのファイルでサーバーとHTMLファイルとコードを共有するやつ
ちょっと説明がし難いがindex.htmlというファイルがあったとして、ファイルプロトコル経由で
file://path/index.html
などのように直接ブラウザで起動すると、プロトコルの関係でファイルが読み込めなかったり、エラーになったりすることがよくある。サーバーをたててlocalhostなどから読み込めばいいのだが、微妙にセットアップが面倒臭い。下記コードをindex.htmlとして保存すると
node index.html
とするだけでブラウザが立ち上がり、index.html自身をルートとしてそのディレクトリ以下のファイルを配信する簡易サーバーが立ち上がる。具体的には、
http://localhost:port/index.html
のような形でアクセスできるようになる。コマンドを実行したディレクトリの絶対パスをもとにポートを自動生成しているので、すでにサーバーが起動済みの場合、
node index.html
は新規にサーバーを立ち上げずに、既存のlocalhost:port/index.htmlをブラウザで開く。 特殊な魔法を使っているので、HTMLとしてもJSとしてもvalidで、ブラウザでもnodeでもコードの一部を共有している。
<!-- (function () { /* --> <!DOCTYPE html> <html> <head> <title>Title</title> <meta charset="UTF-8"> </head> <body> <p>Hello!</p> </body> </html> <script src="index.html"></script> <!-- */ // Shared among server and client sides function mapPort(s) { var hashCode = 0; for (var i = 0; i < s.length; i++) { hashCode += hashCode * 31 + s.charCodeAt(i); } return 49152 + hashCode % 16384; } // Only for browser if (typeof window !== 'undefined') { if (location.protocol == 'file:') { var url = 'http://localhost:' + mapPort(location.pathname); var req = new XMLHttpRequest(); req.open('GET', url, false); try { req.send(null); location.href = url; } catch (ex) { document.writeln("<p>Server is not running. Please run the server by node index.html</p>"); } } return; } // Only for Node.js // Running a simple server. var http = require('http'), fs = require('fs'), child_process = require('child_process'), path = require('path'); var ROOT_DIR = path.dirname(process.argv[1]); function openInBrowser(url) { return child_process.spawnSync('open', [url]); } var port = mapPort(process.argv[1]); var server = http.createServer(function (req, res) { var fPath = path.join(ROOT_DIR, req.url, /\/$/.test(req.url) ? 'index.html' : ''), file = null; try { file = fs.readFileSync(fPath); } catch (e) { // NOOP } res.writeHead(file ? 200 : 404); res.end(file || "File Not Found"); }).listen({port: port }, function () { console.log('successfully established'); openInBrowser('http://localhost:' + port); }).on('error', function () { console.log('already open?'); openInBrowser('http://localhost:' + port); }); })(); // -->
計算量とBig-O記法
プログラマであればアルゴリズムに関する話で、O(n)だとかO(log n)だとか、O(n2)だとか、そういった記号を目にすることはよくあると思う。 なんとなく、log n < n < n2の順に計算量が増加していくとかそういうことも知っていると思うが、計算量の増加とは何か説明しろと言われると、なかなか難しいと思う。この記事では高校数学レベルでわかるように考えていきたい。
まず、計算にかかる時間を、nを入力のサイズとしてf(n)を計算にかかる最大時間を返す関数とすると、計算量一般を、O(f(n))という、入力に対する計算時間の増加率として定義することができる(より厳密には、f:R+ -> R+ where R+=[0,∞)で、a>bであればf(a) >= f(b)であるとき)。
g(n) = n ^ 2
はn = 1に対して1を返す一方、より増加率の低い
h(n) = n + 100
は、n = 1に対して101を返すことに注意したい。言い換えると、関数の増加率の大きさと数字の大きさは違う。
では、どのように比較すればいいかというと、nが十分に大きくなると、増加量が大きい関数(n)>増加量が小さい関数(n)になるという性質を利用する。つまり、nを無限大に近づけて行ったときにおけるlim (n -> ∞)における大きさの違いを利用する。 ここで注意したいのは、正の極限値付近について
lim (n -> ∞) n + 100 = ∞ lim (n -> ∞) n ^ 2 = ∞
であることからわかるように、limによって導き出した∞は極限であって、大小の概念を含む一般の数値と違うことから、大小の比較には使えないということである。ただし、∞と∞の比較を避けることができれば、limを使っても、極値での大きさの比較はできる。つまり、
lim (n -> ∞) g(n) / h(n)
というように、比較したい二つの関数を分数の形にして∞自体を直接比較しなくて済むように変換する。
追記:コメント欄で指摘されたが、上記の定義だと、関数によっては振動して収束しない場合がある。limではなく、limsup/liminfを使って、上極限/下極限について考えると、これらも含めて定義できる。例えば、limsup( n -> ∞) |g(n) / h(n)| < ∞であればg(n) = O(h(n))、など。
この場合、lim(n -> ∞) g(n) / h(n)の値をkとしたとき (g(n)もh(n)も定義により常に正なので0/0は生じないことに注意)
追記2:最初O(f(n)) < O(g(n))と書いていたが、記号の使い方が不適切との指摘を受けたので訂正した。
- kが正の無限大に発散するとき: O(f(n)) ⊊ O(g(n))
- k∈Rでk != 0のとき: f(n) ∈ O(g(n))かつg(n) ∈ O(f(n))
- k=0: O(g(n)) ⊊ O(h(n) となる。
先ほどの例の場合、
lim (n -> ∞) (n^2) / (n + 100) = ∞
となり、nは∞になるので、O(n2)の増加率のほうがO(n + 100)より大きいことがわかる。同様に lim (n -> ∞) (n + 100) / n = 1なのでO(n + 100)とO(n)が同一であることなどもわかる。
promiseを順番に実行する
JavaScript の Promise を返す関数を直列で実行したいので Pinscher というライブラリを作ってみた。 - (define -ayalog '())について。 例えば、非同期に実行されるPromise p1, p2, p3があったとしてcallbackをp1, p2, p3の順番に実行したい場合、reduceが使える。
例1は、p1, p2, p3を並列に実行した上で、callbackの順序を保証したい場合。
まずテスト用に、一定時間まってからvalueでresolveする関数resolveLaterを定義しておく。
function resolveLater(value, ms) { return new Promise(function (resolve) { setTimeout(function () { resolve(value); }, ms); }); }
var promises = [ resolveLater('a', 1000), resolveLater('b', 1000), resolveLater('c', 2000), resolveLater('d', 0), ]; var callback = console.log.bind(console); promises.reduce(function (current, next) { var p = current.then(function (v) { return next; }); p.then(callback); return p; }, Promise.resolve()); // a, b, c, d
このようになる。タスクそのものは並列に実行されるが、callbackは順番に実行される。
逆に、p1, p2, p3自体を順番に実行したい場合、つまり、あるタイミングで同時に動いてるpromiseが1つである必要がある場合。(ネットワークアクセスなどで、負荷をかけたくない場合などはこっちのパターンに近い実装をすることになる)
var argList = [ ['a', 1000], ['b', 1000], ['c', 2000], ['d', 0] ]; var callback = console.log.bind(console); argList.reduce(function (current, args) { return current.then(function () { return resolveLater.apply(null, args).then(function (v) { callback(v); }); }); }, Promise.resolve());
非同期コードのテストについてだが、Jest | Painless JavaScript Unit Testingがおすすめで、jestを使うと、setTimeout系の関数自体を乗っ取って(実行順序を保証しながら)同期的に実行しながらsetTimeout.mock.callsに呼び出された時のmock関数の引数を記録していってくれるので、実際の非同期で不確実な動作に依存せずにテストしやすいコードを書ける。
ただ非同期のテストはどちらにしろ複雑になりがちで、ある程度のところで諦めてテストケースは書くがチェックは手動、という風にするのが現実的なのかもしれない。
追記
コメントでライブラリで解決していることを実現できていないと指摘されたので、その他の例について触れる。
まず、上記コードにはなかった、順番に実行したいpromiseがコード中で動的に追加される場合。 個々のタスク自体は並列に実行するが出力を順番にしたい場合
var callback = console.log.bind(console) var task = Promise.resolve(); var identity = Object; // この場合function (a) { return a; } task = task.then(identity.bind(null, resolveLater('a', 1000))).then(callback); task = task.then(identity.bind(null, resolveLater('b', 1000))).then(callback); task = task.then(identity.bind(null, resolveLater('c', 2000))).then(callback); task = task.then(identity.bind(null, resolveLater('d', 0))).then(callback);
このようにreduceを手動で展開することで動的に追加できるようになる。
もう一つの例として、個々のタスクの実行も順番に(同時に一つしか動かさないように)したい場合
var callback = console.log.bind(console) var task = Promise.resolve(); task = task.then(resolveLater.bind(null, 'a', 1000)).then(callback); task = task.then(resolveLater.bind(null, 'b', 1000)).then(callback); task = task.then(resolveLater.bind(null, 'c', 2000)).then(callback); task = task.then(resolveLater.bind(null, 'c', 0)).then(callback);
このようになる。 Promiseの上にコントロールフローを実装するライブラリを使うより、このように直接promiseを組み合わせる手法を使ったほうがいいと思う理由は複数あって、まず、自由度の点。 例えばここに載せたコードでは、全体の状態がPromiseとして表現されていて取り出すことができる(reduceを直接使った例では、reduceが返すPromiseがそれにあたる)ので、複数のパターンを組み合わせることができる。 具体的には、最初のpromise数個をarrayとして入れておき、動的に実行したいものを後から追加したい場合。
var identity = Object; // この場合function (a) { return a; } var callback = console.log.bind(console); var task = [ resolveLater('a', 1000), resolveLater('b', 1000), resolveLater('c', 2000), resolveLater('d', 0), ].reduce(function (current, next) { var p = current.then(function (v) { return next; }); p.then(callback); return p; }, Promise.resolve()); task = task.then(identity.bind(null, resolveLater('e', 1000))).then(callback);
このようにできる。
reduce相当の処理をしていないコードのもう一つの問題は、エラー処理。動的にタスクを追加する例では全体のエラーという概念は薄いかもしれないが、例えば、記事中のコントロールフローライブラリでは、すべての実行が終わった状態というものをPromiseとして取り出せない。よって、全体のどこかでエラーがあった場合にそれらをcatchする関数、というものが書けない。
全体の処理がPromiseとして表現されている場合は
task.then(null, function (err) { console.error(err); });
などを最後に追加することで、エラー処理が握りつぶされるわかりづらいエラーが防げる。
ES6のclassは巻き上げがされない
ちょっとハマったのでメモ。 例えば
alert(A); function A() { }
はできるが、
alert(A); class A { }
ができないのはなぜか。
class A { } class B extends A { }
例えば、このようなコードの場合、hoisting(スコープ先端への巻き上げ)をしても問題ないが、 関数と違ってextendsはその場で評価しなければいけないことがある。 例えば、
var A = function () { }; A.prototype.x = function () { return 1; }; class B extends A { } alert(new B().x());
というコードの場合、BクラスはAを継承しなければいけないので、var Aで宣言されているものはhoistingされないのでclass宣言もその場で評価しないと、正常に継承することができない。
というわけで、
function A () { }
と
class A { }
は評価の順序が違い、class Aはvar A =とおなじような順序で評価されるわけである。
io.jsとES6とトランスパイラまわりの現状
最近いろいろな環境でES6まわりの環境が整いはじめたので、使っていくために下調べしておこうと思う。
ES6の実装状況であるが、ECMAScript 6 compatibility tableにまとめが載っている。
例えば、io.js (nodeのfork)のES6実装状況。stableなES6 featuresが元から有効になってる。
現在有効なのは、文字列テンプレート、for of、generators, Promise, Map, WeakMap, Setなど。
最新版をターゲットにするのであれば、上の機能は何も考えずにそのまま使うことができる。 このうち、テンプレート、for of, WeakMap以外はpolyfillが手に入るので、それらを使わなければPromise, Mapなどはfeature detectして実装されていない場合はmoduleを読み込むことで幅広いバージョンをサポートできる。
node.jsは--harmonyを有効にしなければジェネレータが使えないなど、node.jsはio.jsとはES6まわりの実装事情が違う。
io.jsでサポートされていない(classes)などの機能を使いたい、あるいは自分で書いているコードでサポートするバージョンのio.js / nodeでネイティブに実装されていない機能を使いたい場合は、トランスパイラを使う。 今一番勢いがあって機能が整ってるのはBabel.js (つい最近まで6to5という名前だった)で、クラスなどのES6の機能はもちろんsource mapsのサポートもあり、React、JSX、Flowなども使用することができる。
一般的なトランスパイラと同様で、
$ npm install --global babel $ babel script.js
とするだけで使うことができるので、gulpなどでコンパイルするといい。
この場合、
dist/*.js src/*.es6.js
のように、ES6のコードは.es6.jsとしておくとわかりやすくて、 npm publishするときは、 dist/側はhookでインストール時に生成されるようにしておくとgit logが汚れない。
source mapsについては、io.js / node側で有効にするには
require('source-map-support').install()
とすれば良い (参考: ES6 at PayPal )。
ReactとJSXを使いたい場合は、もともとreact-toolsはharmonyオプションがあるので、一部機能だけを使えればいい場合は
<script type="text/jsx;harmony=true">
などとして書くかreact-toolsでharmonyオプションを有効にすればいいのだが、Babel (6to5) もJSXをサポートしているので、より完全なES6実装が欲しい場合はBabelを使うとよい。
そんな感じで、タイトルと比べて内容がスカスカではあるが、ここ最近、トランスパイラが急に進化してきてES6が使える環境がいきなり整ってきたような雰囲気があるので、そろそろ使い始めてもいい頃だと思う。いつのまにかブログの更新が数ヶ月空いてしまったけど生きてるので、今年も頑張ってブログを書いていきたい。
JSライブラリ/フレームワークの良い、悪いメモ
※ただのメモで、未来志向なのであまり真に受けてはいけない。
良いっぽい
React.js
早速い/コンポネント志向/APIの設計がいい。JSXと他のトランスパイラの組み合わせという問題はある
Promise
ネイティブに入った、誰もが使ってる
TypeScript
ES6時代でも存在意義のある言語。TypeScript互換のFacebook Flowの動向に注目
Backbone.js
ModelとEventを使う/Viewは使わなくていい
Lodash
Underscore.jsをよくしたやつ
Gulp
Gruntより良いという意味で。browserifyまわりがうまく動かない問題があってnpm runのほうがいいという噂もあるがまあ良いに分類してもいい
EventEmitter
Custom EventはDOMにくっ付いてる感があるのでロジック志向の物にはEventEmitter使ったほうがいい
6to5
Traceurとか色々あるけどSource Mapsあってライブラリ依存が少なくてregenerator使ってるあたりが良い
co
generator/Promise時代のasync
JSHint
JS直接書くならこれがいる
Browserify
クライアントサイドもrequire使うべき
jQuery
UIベタ書きせずBetter DOMとして使う
悪いっぽい
AngularJS
フレームワークがでかい、設計がおかしい
Polymer
ダーティーハックの塊 コメントで指摘があったがハックが多いのはWebComponents(特にShadow DOM)のPolyfill実装のpaltform.jsで、これはpolyfillなのでいずれ消えるそうである。ただ、ヘビーなpolyfill実装に依存しているブラウザが多いので、現時点ではあまり勧められないのも事実だと思う。
Async.js
Promiseを使う
jQuery.Deferred
正しいPromiseを使う
AMD modules
CommonJSを使う
Prototype
だいぶ前に死んだ
jQuery UI / jQuery Mobile
もう役目は終えた
JSLint
JSHintの時代になった/どうでもいいことでエラーを出しすぎる
RequireJS
でかすぎる/AMDの時代は終わった/事前にコンパイルしてくっつけるスタイルが主流
Component
npm使って困ったらbowerで良い
判断しかねる
CoffeeScript
ES6+のclass / arrow function syntaxもろもろと相性が悪い/若干役目を終えつつある感が漂ってる
KnockoutJS
MVVMよりReactJSの設計のほうが綺麗
Meteor
ロックインが激しい/思ったより普及しなかった/まだ発展中
Grunt
Gulpの時代が来てる/gulpのプラグインも埋め込めるようになったから大は小を兼ねる的な使い方もある
Traceur
全体的に重くてコンパイル後がでかい/ES6カバー率は高い
ImmutableJS
見込みはあるけどAPIがまだまだ不安定/あまり使われてない
Get mouse coordinates on Yosemite using JavaScript Automation(ターミナルからマウス座標をゲットする)
英語で調べたけどぜんぜん出てこないので英語で書く。Yosemiteでターミナルからマウス座標をゲットするコマンドはこれ。
osascript -l JavaScript -e "ObjC.import('Cocoa');console.log(JSON.stringify($.CGEventGetLocation($.CGEventCreate(null))))"
In OS X 10.10 (a.k.a. Yosemite), Apple has deprecated Ruby 1.8 along with support for "osx/cocoa" module in favour of AppleScript's AppKit and JavaScript Automation. I found it quite inconvenient since it used to be quite easy to get access to OS X's internal information such as mouse coordinates without needing any external binary files prior to Yosemite. After searching on the Internet for a while, however, I found an alternative way to do it in Terminal using JavaScript Automation introduced in Yosemite. So here's how to do it.
Firstly, open up Script Editor and switch the language from ActionScript to JavaScript. You can now write JavaScript in Script Editor, in which you have access to most of the functionality available in Action Script like Mail.inbox.messages[0].subject()
. You will also get this magical ObjC bridge called $
.
ObjC.import('Cocoa'); $.NSBeep();
(More information available at JavaScript for Automation Release Notes)
You can use the following code to obtain the mouse coordinates:
ObjC.import('Cocoa'); ObjC.import('stdio'); var c = $.CGEventGetLocation($.CGEventCreate($())); $.printf('%d %d\n ', c.x, c.y);
Secondly, there's also a builtin command called osascript, which supports JavaScript as well. Type in
osascript -l JavaScript -e "ObjC.import('Cocoa');console.log(JSON.stringify($.CGEventGetLocation($.CGEventCreate(null))))"
in Terminal, and you'll get something like
{"x":920.4453125,"y":698.9140625}
You can execSync
the above code and then JSON.parse it to get the mouse coordinates in Node.js. I can think of many other interesting things including creating a native app on top of JavaScript Automation, though it will not look so JavaScript-y.
To sum it up, using JavaScript Automation will become a standard now that Apple seems to have lost its interest in Ruby. I think JavaScript Automation is worth investigating because most of the knowledge can be applied to Objective C and other languages that bridge Cocoa APIs.
References: