- 投稿日:2020-04-02T23:30:40+09:00
【カンタン】Vueで縦に自動伸縮するtextareaの作り方 | How to make auto resizing <textarea> by Vue.js
インターネッツで調べてみても中途半端なtextareaしか出てこなかったので、備忘も兼ねた記事です。
伸びるけど縮まないとか、縮むけど1文字いれるごとに2pxずつしか縮まないとか、意味なくない!?!?!?!?!?!?
んおお!?!?!?!?
まずは結果
うおおおおおおおお
うおおおおおおおお
うおおおおおおおお
うおおおおおおおお
うおおおおおおおおうおおおおおおおお
うおおおおおおおお
うおおおおおおおおうおおおおおおおおうおおおおおおおお
うおおおおおおおお
うおおおおおおおお
うおおおおおおおおコード
今気づいたけどマークダウンの候補にvuejsってある。。
便利すぎない?業務で書いたコードからいらないやつ消した感じなので、このまま動かなかったらごめんなさい。
編集リクエスト送っていただければーーーMyTextarea.vue<template> <textarea class="textarea bg-white" :style="textareaStyle" :placeholder="placeholder" :value="value" @input="handleInput($event)" /> </template> <script lang="ts"> import Vue from "vue" export default Vue.extend({ props: { placeholder: { type: String, default: "" }, value: { type: String, default: '' } }, data() { return { textareaHeight: 100 // デフォルト値いれとく。minHeightといっしょでよい。borderあるのでちょっとずれる } }, computed: { textareaStyle(): object { // 動的にtextareaのheightをいじれるようにしている return { height: `${this.textareaHeight}px` } } }, methods: { async handleInput(event: any) { // 入力されるたびによばれる。anyなのはゆるして。。。 this.$emit('input', event.target.value) // これは親に伝えるためだけ。 this.textareaHeight = 0 // ミソ。一旦textareaのheightを0にしちゃう await this.$nextTick() // さらにミソ。ここで待たないとDOMの更新前に下のコードが走って変な挙動になる // heightが0になった瞬間textareaはminHeight: 100になる // 入力済み文字列の高さが100を超えたら、scrollHeightが必要な分だけ大きくなる = それをheightにしちゃえばOK! this.textareaHeight = event.target.scrollHeight } } }) </script> <style lang="stylus" scoped> .textarea-container { width: 100%; } .textarea { width: 100%; min-height: 100px; // ここはお好み。変えるならdata()の値も変えるとよいよ border: 1px solid #D9D9D9 border-radius: 4px; padding: 5px 12px; &::placeholder { color: #D9D9D9 } } </style>TL;DR
this.$nextTickがミソなんじゃぞ。
- 投稿日:2020-04-02T23:24:07+09:00
4歳娘「パパ、20歳以上のユーザーを抽出して?」
ある日、ピクニックで訪れた山にて
娘(4歳)「パパ、見て!」
ワイ「なんや、娘ちゃん」
娘「あそこに、家族風のオブジェクトが落ちてるよ!」
JavaScriptconst family = { mother: { name: "よめ太郎", age: 35 }, father: { name: "やめ太郎", age: 37 }, daughter: { name: "娘ちゃん", age: 4 } };ワイ「おお、ほんまや!」
ワイ「mother、father、daughterの3人家族やな!」娘「せっかくだから、この中から20歳以上のユーザーを抽出してみようよ!」
ワイ「おお!せっかくやからな!」
ワイ「やってみよか!」
ワイ「でも、家族のことをユーザーって呼ぶのはやめてな!」
ワイ「そんで、ええと」
ワイ「大人だけを抜き出したオブジェクトを作ればええんやったな!」
ワイ「ほな、やってみるで!」JavaScriptconst otona = {}; Object.keys(family).forEach(key => { const person = family[key]; if (person.age >= 20) { otona[key] = person; } });娘「わぁ、すごい!」
娘「パパ、このコードがどういう意味だか説明して?」ワイ「おお、ええで!」
ワイ「まず」JavaScriptconst otona = {};ワイ「
otona
って名前の空オブジェクトを作るんや」
ワイ「そこに、family
の中から20歳以上の人間だけを詰め直していくんや」娘「うんうん」
ワイ「そのためにまず」
ワイ「Object.keys(family)
を使って」
ワイ「family
というオブジェクトの、key名たちを配列として取得するんや」娘「オブジェクトの、key名たちを取得・・・」
娘「ってことはつまり、Object.keys(family)
は」
娘「["mother", "father", "daughter"]
と同じってことだね!」ワイ「その通りや!」
ワイ「せやから、↓こんなイメージや」JavaScript["mother", "father", "daughter"].forEach(key => { /* 省略 */ });ワイ「↑こんな感じで、配列の
forEach
メソッドを使って」
ワイ「mother
、father
、daughter
と順番に回しながら」
ワイ「if文で20歳以上かどうか調べて」
ワイ「20歳以上の人間だけをotona
オブジェクトに詰め詰めしていくんや」JavaScript["mother", "father", "daughter"].forEach(key => { // keyには "mother" か "father" か "daughter" が入ってくる。 const person = family[key]; if (person.age >= 20) { otona[key] = person; } });ワイ「↑こんな感じやな」
娘「なるほど〜」
ハスケル子「イマイチですね」
ワイ「!?」
ハスケル子も来ていた
ワイ「おお、ハスケル子ちゃん」
ワイ「ハスケル子ちゃんもこの山に遊びに来とったんか」ハスケル子「はい、私もこの山に関数拾いに来ていました」
ワイ「おお、ほんまに好きやな〜」
ワイ「ところで、さっきのコードの何がイマイチなん?」ハスケル子「うまくは言えませんが、私ならこう書きます」
JavaScriptconst otona = Object.fromEntries( Object.entries(family) .filter(([, person]) => person.age >= 20) );ワイ「
Object.fromEntries()
にObject.entries()
・・・」
ワイ「何なん、このメソッドたち・・・?」ハスケル子「
Object.entries()
は、オブジェクトから配列を作ってくれるやつです」ワイ「オブジェクトから配列を作る・・・?」
ハスケル子「はい」
ハスケル子「例えば、Object.entries(family)
は・・・」JavaScript[ ["mother", { name: "よめ太郎", age: 35 }], ["father", { name: "やめ太郎", age: 37 }], ["daughter", { name: "娘ちゃん", age: 4 }], ]ハスケル子「↑これと同じ意味になります」
ワイ「へぇ〜、オブジェクトからこんな感じの配列を生成してくれるんや・・・」
ワイ「じゃあ、もう一つのObject.fromEntries()
はどんなメソッドなん?」ハスケル子「
Object.fromEntries()
は、さっきのObject.entries()
の逆ですね」
ハスケル子「さっきObject.entries()
で作った配列をObject.fromEntries()
に渡してあげると」
ハスケル子「元のオブジェクトに戻ります」
ハスケル子「つまり・・・」JavaScriptconst obj = Object.fromEntries( Object.entries(family) );ハスケル子「↑こうすると、変数
obj
にはfamily
そのままのオブジェクトが入るってことです」ワイ「ふーん」
ワイ「オブジェクトから一回配列を作って、その配列からまたオブジェクトを作る」
ワイ「そんなことして何が嬉しいん?」ハスケル子「一度配列にすることによって」
ハスケル子「配列の持っているfilter()
やらmap()
やらのメソッドを使って」
ハスケル子「好きなように整形できるのが嬉しいですね」
ハスケル子「今回であれば、20歳以上の人間だけを抽出したい訳ですから」
ハスケル子「filter()
メソッドが使えるじゃないですか」JavaScriptObject.entries(family) .filter(([, person]) => person.age >= 20)ハスケル子「↑こうしてフィルターしてやるんです」
ハスケル子「そうすると・・・」JavaScript[ ["mother", { name: "よめ太郎", age: 35 }], ["father", { name: "やめ太郎", age: 37 }], ]ハスケル子「↑こんな配列が出来上がります」
ハスケル子「この配列をObject.fromEntries()
でオブジェクトに戻してあげると」JavaScript{ mother: { name: "よめ太郎", age: 35 }, father: { name: "やめ太郎", age: 37 } };ハスケル子「↑このように、20歳以上の人間だけが入ったオブジェクトになってる」
ハスケル子「っていう訳ですね」ワイ「おお〜」
まとめ
Object.entries()
を使うとオブジェクトから配列を生成できる。Object.fromEntries()
を使うと配列を元にオブジェクトを生成できる。- オブジェクトから一度配列に変換することで、配列の持っている便利なメソッドたちを使える。
- オブジェクトを色々と整形したい時に便利。
ワイ「・・・↑こういうことやな!ハスケル子ちゃん」
ハスケル子「そんな感じです」
ワイ「おおきにやで!」
ワイ「また一つ賢くなったわ!」〜おしまい〜
- 投稿日:2020-04-02T23:12:22+09:00
久しぶりに create-react-app を使ったら動かなかった話
事象
最近はいつも React Native を書いてるので、たまには React でも触ろうとして、公式に従い
npx create-react-app
コマンドを実行したら、node_modules/ package.json yarn.lockしか存在しないプロジェクトが生成されました。
しかも、package.json
は、{ "name": "PROJECT_NAME", "version": "0.1.0", "private": true, "dependencies": { "react": "^16.13.1", "react-dom": "^16.13.1", "react-scripts": "3.4.1", }, }というシンプル極まりないもの。
グローバルインストールしたcreate-react-app
を削除しても直りません。解決策
GitHub の issue にあるように、再度グローバルインストールをして、
npx create-react-app
を実行すると直りました。久しぶりに触ると色々とトラブルに直面しますね。不幸だ(ぇ
- 投稿日:2020-04-02T22:44:16+09:00
Vue.js でインクリメンタルサーチ
インクリメンタルサーチとは
インクリメンタルサーチ(英語: incremental search)は、アプリケーションにおける検索方法のひとつ。検索したい単語をすべて入力した上で検索するのではなく、入力のたびごとに即座に候補を表示させる
完成品
HTML
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>インクリメンタルサーチ</title> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <link rel="stylesheet" href="style.css"> </head> <body> <div id="app"> <!-- v-model は任意の form 要素にある value、checked、 または selected 属性の初期値を無視します。input または textarea は常に、信頼できる情報源として Vue インスタンスを扱います。 コンポーネントの data オプションの中で JavaScript 側で初期値を宣言する必要があります --> <input type="text" placeholder="検索" v-model="search"> <select v-model="sort"> <option value="">ソート無し</option> <option value="asc">昇順</option> <option value="desc">降順</option> </select> <transition-group tag="ul"> <li v-for="item in sortedList" v-bind:key="item.id"> {{ item.text }} </li> </transition-group> </div> <script src="main.js"></script> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> </body> </html>JS
new Vue({ el: "#app", data:{ search: '', sort: '', list: [ { id: 1, text: 'Python' }, { id:2, text: 'Ruby'}, { id:3, text: 'PHP'}, { id:4, text: 'JavaScript'}, { id:5, text: 'Java'}, { id:6, text: 'Go'}, { id:7, text: 'C'}, { id:8, text: 'C#'}, { id: 9, text: 'Rails' }, { id:10, text: 'Django'}, { id:11, text: 'MySQL'}, { id:12, text: 'Vue.jst'}, { id:13, text: 'react'}, { id:14, text: 'Docker'}, { id:15, text: 'unity'}, { id:16, text: 'jQuery'} ] }, computed: { filteredList: function(){ return this.list.filter(function(item){ return item.text.indexOf(this.search) > -1 }, this) }, sortedList: function(){ var copy = this.filteredList.slice() if(this.sort === 'asc' ){ return copy.sort(this.comparatorAsc) } else if(this.sort === 'desc') { return copy.sort(this.comparatorDesc) } else{ return copy } } }, methods:{ comparatorAsc: function(itemA, itemB){ if(itemA.text < itemB.text){ return -1 } else if(itemA > itemB.text){ return 1 } else{ return 0 } }, comparatorDesc: function(){ if(itemA.text > itemB.text){ return -1 } else if(itemA < itemB.text){ return 1 } else{ return 0 } }, } });CSS
body{ font-family: sans-serif; } input, select{ padding: 2px 8px; font-size: inherit; vertical-align: middle; } ul { position: relative; margin-top: 6px; padding: 0; width: 300px; list-style: none; } li{ margin: 0; padding: 10px; border-bottom: 1px solid #ddd; } .v-move{ transition: transform 300ms ease-out; } .v-enter-active { transition: 300ms; } .v-enter{ opacity: 0; } .v-enter-to{ opacity: 1; } .v-leave-active{ transition: 300ms; } .v-leave{ opacity: 1; } .v-leave-to{ opacity: 0; }
- 投稿日:2020-04-02T21:52:59+09:00
Kinx ライブラリ - Signal
Signal
はじめに
「見た目は JavaScript、頭脳(中身)は Ruby、(安定感は AC/DC)」 でお届けしているスクリプト言語 Kinx。言語はライブラリが命。ということでライブラリの使い方編。
今回は Signal です。
- 参考
- 最初の動機 ... スクリプト言語 KINX(ご紹介)
- 個別記事へのリンクは全てここに集約してあります。
- リポジトリ ... https://github.com/Kray-G/kinx
- Pull Request 等お待ちしております。
簡単ながらシグナルをサポートしました。
Signal
Signal は、
type
としてSignal.SIGINT
とSignal.SIGTERM
のみサポート。Signal.trap(type, handler)
でシグナル・ハンドラを登録し、処理を実行する。ハンドラが登録されていない状態でシグナルを検知したら、プログラムが終了する。Signal.trap(type, handler)
シグナル・ハンドラ(
handler
)を登録する。id を返すので、登録解除したい場合はSignal.remove(id)
で登録したハンドラを解除できる。ハンドラが明示的に false を返した(
return false
した)場合、プログラムを終了する。現時点ではハンドラは登録した順序でコールされるが、この順序を保証するかは決めていない。保証する必要があるかな?
Signal.remove(id)
id で示されたハンドラを解除する。
ブロッキング処理の中断
以下の組み込みメソッドでのウェイト中には検出して中断するようにしている。
System.sleep()
$stdin.getch()
$stdin.readLine()
サンプル
Ctrl-C では終了しないが、Ctrl-Break で終了する。ただし、Ctrl-C を 10 回受信したら、ループから抜けてプログラムを終了させる。
var trapped = 0; Signal.trap(Signal.SIGINT, function() { System.println("SIGINT"); ++trapped; }); Signal.trap(Signal.SIGTERM, function() { System.println("SIGTERM"); return false; }); while (trapped < 10) { System.println("Trapped = %2d" % trapped); System.sleep(1000); } System.println("Ended");結果。
Trapped = 0 Trapped = 0 Trapped = 0 SIGINT Trapped = 1 SIGINT Trapped = 2 SIGINT Trapped = 3 SIGINT Trapped = 4 SIGINT Trapped = 5 SIGINT Trapped = 6 SIGINT Trapped = 7 SIGINT Trapped = 8 SIGINT Trapped = 9 SIGINT EndedWindows で Ctrl-Break、または Linux で
kill -15 [PID]
してみる。Trapped = 0 Trapped = 0 Trapped = 0 SIGINT Trapped = 1 SIGINT Trapped = 2 SIGINT Trapped = 3 Trapped = 3 SIGTERMおわりに
今回は簡単に説明。シグナルは割り込みと同じで急にフローが変化するので、処理前後でつじつま合うようにするために結構手が入りました。内部仕様はもう少し変化するかもしれない。動作仕様は変わらない範囲で。
あと、Windows と Linux で色々仕様が違うのを何とかするのが大変。Linux だと
fgets
とかシグナルで中断されないのでsiginterrupt()
とか使ってシステムコールから復帰させたりしないといけない。ハンドルされるタイミングも微妙に違う。
SIGINT
とSIGTERM
以外サポートする必要あるかな?ではまた次回。
- 投稿日:2020-04-02T21:26:17+09:00
Haskellのプログラムは '現実世界'を引数にとり、プログラムが影響を与えた'現実世界' を返す純粋関数
はじめに
純粋関数型プログラミング言語は純粋なのにどうやって入出力を行うのか概念的にわからない。という質問をよくいただくので、解説をして行こうかなと思います。
タイトルで出落ちです。
対象読者はHaskellを学びたてだったり、ML型の言語をちょっと触ったことがある程度の人ですが、
解説の為にJavaScriptを多用しますので、JavaScriptが読めれば楽しめるかと思います。これを読んでコードをかけるようにはならないとは思いますが、概念的に面白いし実用性あるじゃん〜と思ってもらえればいいなと思います。
概念的な説明の簡単のために、正確ではない表現が登場しますが、
概念的な説明の簡単のためになってなく、正確ではない表現があった場合は優しく指摘していただけると幸いです。純粋関数型プログラミングとは
純粋関数型プログラミング言語と呼ばれる言語が存在します。純粋関数型プログラミング言語は記述する関数を副作用のない純粋な数学的関数として定義し、プログラムを構築します。
純粋関数などの例
足し算をする純粋関数
JavaScriptfunction add(n, m) { return n + m; }足算をして表示をする関数 こちらは純粋ではない関数
JavaScriptfunction add(n, m) { console.log(n + m); }コンソールから数値を受けて足算をする関数 こちらも純粋関数ではない
JavaScriptfunction add() { const n = console.read(); const m = console.read(); return n + m; }※
console.read()
は冗長になってしまうのでイメージ純粋関数型言語では、副作用与えうる関数や入力によって出力が決定しない関数は関数と呼びません。
上記の例では計算をした上でconsoleに表示したり、consoleからreadして値を返す関数などです。HaskellのHello world!
Haskellではコンソールで文字を読み込んで出力するプログラムはこのように記述します。
Haskellmain :: IO () main = do x <- getLine print x関数なのにめっちゃ入出力行ってんじゃん!!全然純粋じゃなく見える!!!
エントリーポイントであるmain関数はIO ()というよくわからない型になってますね。
最初はC言語のint main()
return 0;
ぐらいに思っておけばいいですが、なかなか面白い概念を包み込んでいます。この概念を解説していきます。状態遷移
IO ()
型を解説する前に、Haskellにおける状態遷移を関数で表す方法を知る必要があります。純粋関数型プログラミング言語では、入出力だけでなく変数の値を変更することもできません。
純粋関数しかないため、純粋関数から関数外の変数の値を変更したらそれはもう純粋関数ではないからです。しかしながら純粋関数型言語でもこの状態遷移を関数で表すことができます。
まずは一般的なJavaScriptのプログラム
何度カウンターを使ったかを返すプログラム 純粋関数ではない
JavaScriptlet state = 0; function count() { state = state + 1; } function stateTransition() { count(); count(); count(); count(); } // 結果 const result = state;純粋関数のみで状態遷移を表現するプログラム
JavaScriptfunction count(state) { return state + 1; } function stateTransition(state) { return count(count(count(count(state)))); } // 結果 const result = stateTransition(0);純粋関数型プログラミングでは、状態を関数の引数と返り値として持ち回すことで状態を表現できます。
しかし、状態を持ち回すのは状態を必要とする関数全てに新たに引数と返り値を追加せねばいけなく非常に冗長になってしまいます。そこでHaskellではStateモナドと呼ばれる関数の型?DSL?が存在し、状態遷移を簡潔に純粋関数のまま扱うことができます。
code-state2を実現するHaskellのプログラム
Haskell-- 状態を遷移させる関数 count :: State Int () count = modify (+ 1) -- 状態を遷移させる関数を逐次実行 stateTransition :: State Int () stateTransition = do count count count count -- 状態を遷移させる関数の引数に0を入れて遷移させた後の結果を取得 result :: Int result = execState stateTransition 0Stateを引き継いで渡す作業を、Stateモナドの文脈の中では順番に書くだけでよくなります。
JSでやってたこの処理を
JavaScriptfunction stateTransition(state) { return count(count(count(count(state)))); }こう書ける!!
HaskellstateTransition :: State Int () stateTransition = do count count count countHaskellでは関数呼び出しに()などは必要ないことなどを考慮すると、
副作用のあるJavaScriptの例にかなり似ていることがわかります。JavaScriptfunction stateTransition() { count(); count(); count(); count(); }状態を利用したプログラム
ただこのままだと状態を遷移させているだけで状態を利用して何かをしたいということが何もできないですね。
状態を利用しつつ、状態を遷移させていくプログラムをサンプルで書きます。扱いたい処理は先ほどの状態をカウントして値を増やしつつ、返り値として数字を文字列にしたモノにしましょう。
非純粋なJavaScriptで書いてこんな感じ。JavaScriptlet state = 0; function count() { state = state + 1; return state.toString(); } function stateTransition() { const one = count(); const two = count(); const three = count(); const four = count(); return one + two + three + four; // "1234" }これを純粋関数のみでJavaScriptで表します。
JavaScriptfunction count(state) { return [state + 1, state.toString()]; } function stateTransition(state) { const [state1, one] = count(state); const [state2, two] = count(state1); const [state3, three] = count(state2); const [state4, four] = count(state3); return one + two + three + four; } // 結果 const result = stateTransition(0);このように状態を引数にとって、状態と欲しい値をセットで返す->そして次の関数には状態を渡す を繰り返していけば状態を利用したり状態を変更したりしながら、純粋関数のみで計算を行うことができます。
Haskellでは先述のように、Stateモナドを利用して状態を意識せずに次から次へと移していくことが受け継いでいくことができます。
State Int String
という型はInt
は状態の型で、引数と返り値に存在し、Stringは返り値にのみ存在するという型の別名です。
Int -> (Int String)
これの別名みたいなもんです。Haskellcount :: State Int String count = do modify (+ 1) state <- get return (show state) stateTransition :: State Int String stateTransition = do one <- count two <- count three <- count four <- count return (one ++ two ++ three ++ four) result :: String result = evalState stateTransition 0こちらも非純粋関数で書いたJSの状態遷移の計算とかなり似てますね。
JavaScriptでいう、この純粋関数で表している状態が絡んだ計算を、
JavaScriptfunction stateTransition(state) { const [state1, one] = count(state); const [state2, two] = count(state1); const [state3, three] = count(state2); const [state4, four] = count(state3); return one + two + three + four; }Haskellでは純粋関数のまま簡潔に書ける!
HaskellstateTransition :: State Int String stateTransition = do one <- count two <- count three <- count four <- count return (one ++ two ++ three ++ four)非純粋に書いた時とすごくよく似てる!!
JavaScriptfunction stateTransition() { const one = count(); const two = count(); const three = count(); const four = count(); return one + two + three + four; }さて、そろそろ純粋関数のみで状態が絡んだ計算を記述するイメージは掴めてきたのではないでしょうか。
状態が絡む計算まとめ
状態を引数として関数でもらって、状態と状態を利用した結果をセットで返り値として返すだけで状態が絡んだ計算ができる。
Haskellでは逐次実行しているように見える関数も、実は純粋関数を関数合成しただけである。
入出力
状態遷移の続きとして、ちょっとした遊びをします。
状態としての配列と、値として文字列があります。
putという関数を定義します。
状態としての配列と文字列を受け取り、
配列に文字列を追加して、
状態としての配列と '何もない' を返す型です。JavaScriptfunction put(state, string) { state.push(string); return [state, null]; }次にgetという関数を定義します。
状態としての配列を引数として受け取り、
配列の最後の文字列と、状態としての配列を返します。JavaScriptfunction get(state, string) { const end = state[state.length - 1]; return [state, end]; }これを使って遊んでみましょう。
JavaScriptfunction main(state1) { const [state2, str] = get(state1); const [state3, _] = put(state2, str); return state3; } main(["hello"]);最後の状態であるstate3は、
["hello","hello"]
になりますね。Haskellで全く同じ動作をするputとgetです。
非純粋なJavaScriptの例は書きませんが、先述2例のように似ているはずです。
Haskellget' :: State [String] String get' = do state <- get let str = last state return str put' :: String -> State [String] () put' str = modify (++ [str])遊んでみましょう。
Haskellmain' :: State [String] () main' = do str <- get' put' str result = execState main' ["hello"]最初の状態として、引数["hello"]を与えてあげると、
["hello", "hello"]
という最終状態になります。何かに似てきました。チラっ
haskellの標準入力を受け取って、標準出力をそのまま返すプログラムです
Haskellmain :: IO () main = do str <- getLine print strこのプログラムのコンソールの結果をみてみましょう
$ ./echo hello hello
先ほどのputとget関数で遊んだ入力や結果
最初の状態として、引数["hello"]を与えてあげると、
["hello", "hello"]
が帰ってきます。と
echo-exe
を実行したときのコンソールの画面がどことなく似ていませんか?IO型は、
hello
と書かれたコンソールが存在する世界
を状態として引数にとって、hello hello
と書かれたコンソールが存在する世界
を返す純粋関数の型だったんだ!!!!IO型は '現実世界'を引数にとり、プログラムが影響を与えた'現実世界' を返す純粋関数
State T U
の状態Tを現実世界に特殊化した型をIO U
型、
状態ではない結果の型U
を何もないことを表す()
に特殊化すると
IO ()
型となります。
Haskellのmain関数の型ですね。そうやってみてみると、main関数の型は
Haskell-- main :: State RealWorld a main :: RealWorld -> (RealWorld, ())こんな感じになって、確かに現実世界を引数にとって、現実世界を戻り値に持っているんだなとわかります。
何言ってんだこいつと思われるかもしれませんが、実装も本当にそのテイになっています。
Haskellnewtype IO a = IO (State# RealWorld -> (# State# RealWorld, a #))この
IO ()
であるmain関数にHaskellランタイムは現実世界を注入して、現実世界を書き換えてくれる純粋関数だったんですね。まとめ
純粋関数型プログラミング言語であるHaskellは、解説の他に因果的決定論を持ち込む、IOを剥がすunsafeな処理を行わない、などの条件を色々付け加えれば入出力も純粋関数として見ることができるのではないでしょうか?
余談
モナドって難しそうだけどStateモナドの場合は中身がただの関数だヨ
純粋関数型の言語でもこうやって普通の動的なスクリプト言語であるJavaScriptのように簡潔にコードが書けるんだネ
(状態を取り扱っていたりIOを扱っていたりが型で制限できるので更に利点も多い)StateとかIOだけじゃなくて非同期計算やNothingのChainingなどほとんど全ての計算構造を簡単に書けるようにしてくれるので興味を持ったらHaskellやってみてね✨
(強いHaskellerの皆さん、心理的安全性の高いツッコミ待ってます(心が弱いので))
- 投稿日:2020-04-02T21:03:07+09:00
javascriptで入力キーを取得(メモ)
1.まずはコンソールに出力
// キーが離れたときにコンソールに出力 window.addEventListener("keyup",(e) =>{ console.log(e.key); });キーが離れたとき(
keyup
)コンソールに出力します。2.文字を画面に出力
<body> <!--ここに文字を出力させる--> <div id="output"></div> <script> // 要素取得 const output = document.querySelector("#output"); window.addEventListener("keyup",(e) =>{ // keyをHTMLで出力 output.innerHTML = e.key; }); </script> </body>入力したキーが一文字づつ出力されます。
3.入力した文字列を画面出力(配列)
<body> <div id="output"></div> <script> const array =[]; //文字格納用の配列 const output = document.querySelector("#output"); window.addEventListener("keyup",(e) =>{ array.push(e.key); //配列の後ろに追加 output.innerHTML = array; }); </script> </body>配列にプッシュ
push()
します。文字がどんどん追加されます。4.入力した文字列を画面出力
<body> <div id="output"></div> <script> const array =[]; const output = document.querySelector("#output"); window.addEventListener("keyup",(e) =>{ array.push(e.key); const str = array.join(""); //配列を文字列に変換 output.innerHTML = str; }); </script> </body>上の例だと
a,r,r,a,y
みたいに出力されるので、配列の結合にjoin()
を使用します。
- 投稿日:2020-04-02T20:22:59+09:00
vueの勉強し直し
Javascriptのフレームワーク「vue.js」について書いていきます。
まずは Hello World
<div id="app">{{ message }}</div>const app = new Vue({ el: '#app', data: { message: 'Hello World' } })変数を
{{}}で囲むことでHTMLとして表示することができます。
双方向データバインディング
vueと言ったらこれが最初に思い浮かびました。
初めて実装したときは感動しました。
<div id="app"> <p>{{ inputText }}</p> <input v-model="inputText"> </div>const app = new Vue ({ el: '#app', data: { inputText: '' } })テキストフィールドで入力した文字がリアルタイムでテキストとして反映されます。
メソッドも追加できます
<div id="app"> <p>{{ count }} 回クリックされました!</p> <button v-on:click="counter">押してね</button> </div>const app = new Vue ({ el: '#app', data: { count: 0 }, methods: { counter: function(){ this.count++ } } })vanillaや、jQueryのようにclickなどのイベントのメソッドも設定することがでいます。
<button @click="counter">押してね</button>と書き換えることができます。
- 投稿日:2020-04-02T19:39:32+09:00
プリコネR 防御力計算機 (Vue.js + Firebase)
概要
- Vue.jsとFirebaseを使って簡単なサービスを作ってみました。
- プリコネRの攻撃力と与ダメージから防御力を計算するツール。
- リンク:https://r-tools-69dd3.web.app/
ソースコード
index.html︙ <div class="calc"> <p>攻撃力:<input type="text" v-model="attack"></p> <p>ダメージ:<input type="text" v-model="damage"></p> <p>防御力:<input type="text" v-model="defense"></p> </div> ︙js/main.js(function(){ 'use strict'; let vm = new Vue({ el: '#app', data: { attack: 5000, damage: 5000, }, computed: { defense: { get: function () { return Math.round(100 * (this.attack / this.damage - 1)) }, set: function (defense) { this.damage = Math.round(this.attack / (1 + defense / 100)) } }, } }) }())アプリの公開
Firebase(無料枠)で公開しています。こちらの記事を参考にさせていただきました。
Vue.js + FirebaseでTodoアプリを作る参考文献
- 投稿日:2020-04-02T19:39:32+09:00
プリコネR 防御力計算機 (Vue.js+Firebase)
概要
- Vue.jsとFirebaseを使って簡単なサービスを作ってみました。
- プリコネRの攻撃力と与ダメージから防御力を計算するツール。
- アプリ:https://r-tools-69dd3.web.app
ソースコード
index.html︙ <div class="calc"> <p>攻撃力:<input type="text" v-model="attack"></p> <p>ダメージ:<input type="text" v-model="damage"></p> <p>防御力:<input type="text" v-model="defense"></p> </div> ︙js/main.js(function(){ 'use strict'; let vm = new Vue({ el: '#app', data: { attack: 5000, damage: 5000, }, computed: { defense: { get: function () { return Math.round(100 * (this.attack / this.damage - 1)) }, set: function (defense) { this.damage = Math.round(this.attack / (1 + defense / 100)) } }, } }) }())アプリの公開
Firebase(無料枠)で公開しています。こちらの記事を参考にさせていただきました。
Vue.js + FirebaseでTodoアプリを作る参考文献
- 投稿日:2020-04-02T19:39:32+09:00
Vue.js+Firebaseで簡単なWebサービスを作って公開する
概要
- Vue.jsとFirebaseを使って簡単なサービスを作ってみました。
- プリコネRの攻撃力と与ダメージから防御力を計算するツール。
- アプリ:https://r-tools-69dd3.web.app
ソースコード
index.html︙ <div class="calc"> <p>攻撃力:<input type="text" v-model="attack"></p> <p>ダメージ:<input type="text" v-model="damage"></p> <p>防御力:<input type="text" v-model="defense"></p> </div> ︙js/main.js(function(){ 'use strict'; let vm = new Vue({ el: '#app', data: { attack: 5000, damage: 5000, }, computed: { defense: { get: function () { return Math.round(100 * (this.attack / this.damage - 1)) }, set: function (defense) { this.damage = Math.round(this.attack / (1 + defense / 100)) } }, } }) }())アプリの公開
Firebase(無料枠)で公開しています。こちらの記事を参考にさせていただきました。
Vue.js + FirebaseでTodoアプリを作る参考文献
- 投稿日:2020-04-02T18:41:59+09:00
ハンバーガーメニューの作成
HTML5
<body> <!-- ハンバーガーボタン --> <div class="ham" id="ham"> <!-- ハンバーガーボタンのライン --> <span class="ham_line ham_line1"></span> <span class="ham_line ham_line2"></span> <span class="ham_line ham_line3"></span> </div> <!-- メニューの中身 --> <div class="menu_wrapper" id="menu_wrapper"> <div class="menu"> <ul> <li><a href="#1" style="color: #FFF;">メニュー1</a></li> <li><a href="#2" style="color: #FFF;">メニュー2</a></li> <li><a href="#3" style="color: #FFF;">メニュー3</a></li> </ul> </div> </div>CSS
.ham{ position: relative; width: 40px; height: 40px; cursor: pointer; background-color: rgba(0, 0, 255, 0.911); } /* ハンバーガボタンのライン */ .ham_line { transition: all 0.3s; position: absolute; left: 10px; width: 20px; height: 2px; background-color: #FFF; } .ham_line1 { top: 10px; } .ham_line2 { top: 18px; } .ham_line3 { top: 26px; } /* Javascriptでクリックイベントが発火した後の処理 ハンバーガボタンのライン */ .clicked .ham_line1{ transform: rotate(45deg); top: 20px; } .clicked .ham_line2{ width: 0px; } .clicked .ham_line3{ transform: rotate(-45deg); top: 20px; } /* メニューの表示 */ .menu { position: fixed; left: -400px; width: 300px; height: 300px; background-color: rgba(0, 0, 255, 0.849); transition: all 0.5s; color: #FFF; } .clicked .menu { left: 8px; }JavaScript
const ham = document.querySelector('#ham'); const menu_wrapper = document.querySelector('#menu_wrapper') // addEventListener さまざまなイベント処理を実行することができるメソッド ham.addEventListener('click', function(){ // classListとは、対象要素に設定しているクラスを配列のように扱えるオブジェクト ham.classList.toggle('clicked'); menu_wrapper.classList.toggle('clicked') });addEventListener
classListとは
- 投稿日:2020-04-02T18:17:08+09:00
JavaScript : 一定範囲のスクロールを検知するしてクラスを付与・除去する
- 指定した要素がブラウザ表示範囲の頂上に来たらクラスを付与する
- 指定した要素がブラウザの表示範囲からでたらクラスを除去する
という仕様でビジュアルコントロールをするスクリプトです。
言葉だけでは分かりづらいので以下サンプル画面をスクロールしてみてください。See the Pen range activate script by Kazuyoshi Goto (@KazuyoshiGoto) on CodePen.
「Dummy Text Section 1」のブロックが表示範囲最上部に来たらナビゲーションが出てきます。
「Dummy Text Section 1」が画面外に消えるまでスクロールするとナビゲーションが引っ込みます。ナビゲーションのレイヤーを「show」というクラスの付与・除去でコントロールしています。
「一定範囲だけである要素を表示させる」という、よくある感じのUIです。こういう細かいネタって案外明文化されていないのでメモしておこうと思います。
JavaScript
window.onscroll = function () { scrollToggleClass(".section1", ".fixed-nav", "show"); } function scrollToggleClass(rangeTarget, addTarget, classname) { if($(rangeTarget).length){ scroll = $(window).scrollTop(); startPos = $(rangeTarget).offset().top; endPos = startPos + $(rangeTarget).outerHeight(); if (scroll > startPos && scroll < endPos) { $(addTarget).addClass(classname); } else { $(addTarget).removeClass(classname) } } }スクロールを検知したら
scrollToggleClass()
を動かす処理です。
scrollToggleClass()
には範囲対象となるrangeTarget
、クラスの取り外しをするaddTarget
、付与するクラス名classname
を指定します。
rangeTarget
の開始位置と終了位置を取得し、あとはウィンドウのスクロール位置に応じてクラスを付けたり外したりしています。
- 投稿日:2020-04-02T18:08:21+09:00
moment.jsでミリ秒を取得する方法
moment.js 便利だお。
// 現在日時の場合 const millisecond = moment().valueOf()// 日付を指定する場合 const millisecond = moment(new Date('2020-04-02')).valueOf()ちなみに、momentオブジェクトが正しい日付形式になっていないと
.valueOf()
で取得した値はNaNになります(ハマりました)。
- 投稿日:2020-04-02T17:57:55+09:00
初心者がオンライン競プロに出てみた
何故
最近、自分のJavaScriptのレベルはどれくらいなんだろうと思いpaizaでスキルチェックを何度か受けていたのだが、ある記事で「AtCoder」なるサイトが紹介されていて気になったので何も準備せずに参加してみた。
標準入出力がムズイ
何も準備しないとはいっても参加登録をしなければ始まらないので一応サインアップと今日(3/28)のコンテストの参加登録をした。開始時間の21時になると、サイト上に「コンテストが始まりました」というメッセージと共に6問の問題が表示され、制限時間のカウントダウンが始まる。始まる前は「paizaと大して変わらないだろう」とたかを括っていたのだが、いざ始まるとpaizaと違って標準入出力を自力でやらなければならないので何から始めて良いか分からず、結局標準入出力のミスのせいでランタイムエラーを10回以上繰り返し、便意による妨害もあって制限時間1時間40分中半分以上を無駄にするという大失態を犯してしまった。
これからAtCoderに挑戦しようと思っている方に伝えたい。AtCoderのサンプルコードを熟読するなりコピペするなりして、始まる前に標準入出力のコーディングだけは身に付けた方が良い。問題の難易度は高くないが...
標準入出力を乗り切り問題に取り掛かってしばらくすると、あまり問題自体は難しくないことに気がついた。情けない時間切れで3問しか解けず4問目も最後にチラッと見ただけなのだが、少なくとも3問目までは初心者の僕でもホワイトボードを使うことなく脳内で解けた。僕の現在のpaizaランクがBなので、それくらいのレベルの人であれば4問目まで解くのはそう難しくないだろう(標準入出力を予習しておけば)。
しかし、一つだけ注意しておかなければならない点がある。時たま、paizaで通ってAtCoderで通らないコードがあるということだ。例えば、n.sort( function(x,y){ return (x < y ? -1 : 1);//降順の場合は-1と1を逆にする } );上のコードは配列nを昇順に並べ替える際のJavaScriptコードで、僕はいつもこれをpaizaで乱用しているのだが、AtCoderでこのまま使うとランタイムエラーが出る。最終的に
function func(a, b) { return a - b; } n.sort(func);という感じで関数を外に出して何とか乗り切ったが、今後paizaと違うコーディングをしないといけないとなるとなかなかキツイ。片方でしか通らないことの理由が分からないのもまたツライ。
結果
最終結果は6問中3問正解でスコアは600点だった。スコアが何の参考になるのかは分からないが、始まる前に何もしないでたかを括るほど楽ではないし、ビビってちびるくらい難しいものでもないということが分かった。まあ競技プログラミングというのはそもそも速さを競うものだから当たり前かもしれないが。
レーティング...
今回僕は初コンテストだったのでレーティングがどういうものでどれくらいだと凄いのか全く分からなかったが、今日のコンテストで僕のレーティングは11になった。初心者の証である灰色から中級者の茶色に進級するにはレーティングが400必要らしい。。。。マジ?
ぶっつけ本番で挑んだAtCoderだったが、初心者にしては楽しめた方だったと思う。ただ僕にとってはネットワークアーキテクチャの勉強の方がやりたい事だしやるべき事でもあるので、競プロごっこは週一程度に抑えて、マイペースに茶色目指して頑張ろうと思う。
最後に、標準入出力だけは予習しよう
筆者情報
高校生。
ポートフォリオはこちら
この記事は僕のnoteからの転載です
- 投稿日:2020-04-02T14:47:04+09:00
GASを使っていく中で便利だと思った機能(メモ)
概要
GASを使っていろいろやっていく中で個人的に便利だと思った機能をまとめていきます。
キャッシュ
GASの処理を高速化する中で必要になって来るのがGASメソッドの呼び出し回数をいかに減らすか。。
特にカスタム関数に関しては30秒という制約が契約プラン問わずあるので複雑な処理を実装した際にとても助かりました。
https://qiita.com/golyat/items/ba5d9ce38ec3308d3757コールバック関数
クライアント側からサーバー側のメソッドを呼び出すことが出来るのでUI操作の必要な分岐処理など使い道は様々。
https://tonari-it.com/gas-web-app-google-script-run/V8 Runtimeのサポート
機能と言うわけではないですがこの変更が入ったことで幅が広がりました。
https://officeforest.org/wp/info/google-apps-script%e3%81%8cv8-runtime%e3%82%92%e3%82%b5%e3%83%9d%e3%83%bc%e3%83%88/
- 投稿日:2020-04-02T12:41:01+09:00
Vue.js 算出プロパティとメソッドの違い
はじめに
Vue.js テンプレート制御ディレクティブ まとめの続きです。
今回は算出プロパティとメソッドの違いを学習していきます。
算出プロパティcomputed
算出プロパティ
computed
は、関数によって算出したデータを返す事ができるプロパティ。例えば、以下のようにマスタッシュ構文内に複雑なロジックを書くと可読性が悪くなる。
<!-- 文字列を反転する式 --> {{ message.split('').reverse().join('') }}
こういった複雑なロジックを実行する時に、算出プロパティを利用することが推奨される。
また、ロジックの再利用性を高めたい時などにも利用できる。
computed
を利用してコードを書いてみる。<div id="app"> <p> {{ reverseMessage }} <!-- !sj.euV olleH --> </p> </div> <script> var app = new Vue({ el: '#app', data: { message: 'Hello Vue.js', }, // 算出プロパティの定義 computed: { // メソッドの定義 reverseMessage() { return this.message.split('').reverse().join('') } } }) </script>あれ?これ
methods
と何が違うの・・・
computed
とmethods
それぞれの違いを深掘りする。算出プロパティとメソッドの3つの違い
①プロパティとメソッド
computed
はプロパティなので()
が不要。
methods
はメソッドなので()
が必要。<!-- computed --> {{ reverseMessage }} <!-- methods --> {{ reverseMessage() }}②getterとsetter
computed
はgetter
とsetter
を定義する事ができる。
methods
はgetter
しか定義できない。③キャッシュ
computed
はキャッシュされる。
methods
はキャッシュされない。
methods
は呼び出されるたびに関数の処理を行うまとめ
実行したい処理の内容によって
computed
かmethods
か使い分けるのが良さそう。しかし、現状どういったケースで使い分けるかイメージが沸かない。
この辺りは、実際にアプリケーションを開発しながら身に付けるしかなさそう。
知見が深まり次第、追記していきます
更新履歴
Vue.jsの基本的な使い方まとめ
Vue.jsでTO DOアプリを作る
Vue.js テンプレート制御ディレクティブ まとめ
Vue.js 算出プロパティとメソッドの違い 今ココ
- 投稿日:2020-04-02T11:54:28+09:00
biwascheme call/cc 非同期処理と大域脱出
- 投稿日:2020-04-02T08:33:32+09:00
新型コロナ感染箇所マップを作ってみる【FastAPI/PostGIS/deck.gl(React)】(データ表示編)
こちらの記事は新型コロナ感染箇所マップを作ってみる【FastAPI/PostGIS/deck.gl(React)】(データ加工編)の続きです。
上の記事をご覧になってからお読みください!
Reactの環境構築
ここまででcsvの元データをAPIからGeoJSON形式で吐き出せるようになっていると思いますので、そのデータを利用して地図上にコロナウイルス感染者の位置を表示できるようにしていきましょう!
まずはReactのプロジェクトを簡単に作成できる
create-react-app
を実行するためのNode.js
がインストールされているか確認しましょう$node -v v12.13.1 $npm -v 6.12.1上記2つのコマンドを入力してバージョンの数字が表示されればインストールされています。
コマンドがないよー!なんて怒られた場合にはインストールされていませんのでNodebrewでNodeをインストールするなんかを参考にして
nodebrew
のインストール→nodebrew
からNode.js
をインストール→npm
のインストールを行っていきましょう!
npm
のインストールまで終わったらcovid_sample
に移動し、以下のコマンドでReactのプロジェクトを作成していきます。$npm install -g create-react-app $npx create-react-app .これで
covid_sample
自体がReactのプロジェクトになります。(コマンド最後の.
はカレントディレクトリにプロジェクトを作成するという意味で、ここに何か任意の名前を入れると、covid_sample
以下にその名前でディレクトリが作成されます)$npm startのコマンド入力後、
localhost:3000
へブラウザからアクセスし、以下のような画面が表示されればプロジェクトの作成成功です!deck.glの環境構築
deck.glは自動車配車サービスのUberが作成した大規模なデータセットの視覚的・探索的データ分析のためのWebGLを利用したフレームワークで、React上で動作させることを想定されています。(最近はpureJSでもちゃんと動くみたいですが)
めっちゃかっこいいです。
データさえあれば、以下のサンプルのように地図上に美しくデータを表現することができます。
今回はこのdeck.glを利用して地図上にデータを表示させていきましょう!
(ちなみに!!!!!今回扱うデータはただのポイントデータ(箇所ごとに緯度経度の位置情報があるだけのデータ)なので上のサンプルのようにかっこよくはなりません!!!!!!!すいません!!!!)
deck.glを利用するために以下の2つのコマンドを入力しましょう。
$npm install deck.gl $npm install react-map-gl下の
react-map-gl
はこちらもUberが作ったもので、Mapbox GL JS
というWebGIS界隈では非常に有名なライブラリのReact用ラッパーになります。今回は背景地図の表示に利用していきます。
これでいよいよ開発環境が整ったので、アプリケーションを作成していきましょう!
アプリケーションの作成
mapboxの登録とトークンの発行
mapboxとは、Google マップのようなサービスですが、JavaScriptやネイティブ用のSDKが公開されており、自由に、簡単にカスタマイズできる地図サービスのことです。
今回は背景地図としてmapboxを利用していきますので、トップページから会員登録・マイページに表示されるトークンをコピーしておいてください。
背景地図の表示
src
というディレクトリが出来ているはずなのでそちらに移動しましょう。Reactのプロジェクトでは基本的にこの
src
以下のファイル群をいじっていきます。
src
ディレクトリの構成は以下のような感じになっていると思いますが、今回編集するファイルはApp.js
のみになります。tree. ├── App.css ├── App.js ├── App.test.js ├── index.css ├── index.js ├── logo.svg ├── serviceWorker.js └── setupTests.jsということでゴリゴリ修正していきましょう!
src/App.js// Reactのインポート import React from 'react'; // deck.glを利用 import DeckGL from '@deck.gl/react'; // geojsonを使うのでGeoJsonLayerを利用 import {GeoJsonLayer} from '@deck.gl/layers'; // 背景地図の表示にMapbox GL JSのラッパーを利用 import {StaticMap, Popup} from 'react-map-gl'; // 先ほど取得したトークンを文字列で格納 const MAPBOX_ACCESS_TOKEN = <MAPBOX_ACCESS_TOKEN>; // 初期表示位置などを指定 const initialViewState = { longitude: 139.7212733, latitude: 35.6606213, zoom: 8, pitch: 45, bearing: 0 }; // 最近は関数ベースのコンポーネントが主流だが、サンプルに合わせてクラスベース class App extends React.Component { // コンストラクターでデータを格納するstateを定義 constructor(props) { super(props); this.state = { geojsonPoint: null, }; } // コンポーネントがマウントされてから動作するメソッド // APIにアクセスしてデータを取得しておく componentDidMount() { fetch("http://0.0.0.0:8000/") .then(res => res.json()) .then( (result) => { this.setState({ geojsonPoint: result }); console.log("result", result); }, (error) => { console.log(error) } ) } // ポップアップを表示させるためのメソッド _renderTooltip() { // hoveredObjectはホバーしている地物の情報 const {hoveredObject} = this.state || {}; console.log("hoveredObject", hoveredObject); if (hoveredObject !== undefined) { return ( // react-map-glのPopupコンポーネントを利用してポップアップを表示 <Popup longitude={hoveredObject.geometry.coordinates[0]} latitude={hoveredObject.geometry.coordinates[1]}> <div> <p>id:{hoveredObject.properties.id}</p> <p>年齢:{hoveredObject.properties.age}</p> <p>確定日:{hoveredObject.properties.fixed_data}</p> </div> </Popup> ) } } // レンダリング用のメソッド render() { // コンストラクターで定義したstateからGeoJSONを取得 const geojsonPoint = this.state.geojsonPoint; console.log("geojsonPoint: ", geojsonPoint); const layers = [ new GeoJsonLayer({ // 任意のid id: 'point_layer', // GeoJSONを指定 data: geojsonPoint, // pointの半径 getRadius: d => 2000, // 地物のカラーをRGBaで指定 // aは透過度で0~255から指定 getFillColor: d => [245, 36, 36, 150], pickable: true, // 地物にホバーした時に発生するイベント // stateを更新する onHover: info => this.setState({ hoveredObject: info.object, }) }), ]; return ( <> {/*DeckGLコンポーネントに必要な情報を渡す*/} <DeckGL initialViewState={initialViewState} controller={true} layers={layers} > {/*利用したいmapboxのスタイルとトークンを渡す*/} <StaticMap mapStyle="mapbox://styles/mapbox/dark-v9" mapboxApiAccessToken={MAPBOX_ACCESS_TOKEN}> {/*ポップアップ用の関数も入れる*/} {this._renderTooltip()} </StaticMap> </DeckGL> </> ); } } export default App;Reactの基本的な解説などは別記事に書こうと思っていますが、Reactでは基本的にコンポーネントの戻り値はJSXというJavaScript内にHTMLを埋め込むような記法で書いていきます。
なので、Appクラスのrenderメソッドの戻り値はHTMLのタグのようなものがずらっと並んでいます。
しかし、実際にはトランスパイルされてJavaScriptに変換されるので
<DeckGL initialViewState={initialViewState}></DeckGL>のようにタグの中にpropsと呼ばれる変数のようなものを渡して表示を動的に切り替えることが出来ます!
その他、どういう処理を行なっているのか…という部分はコード中にコメントを書いているので参考にしてみてください!
注意する点としては
- 新型コロナ感染箇所マップを作ってみる【FastAPI/PostGIS/deck.gl(React)】(データ加工編)で作成した、dockerコンテナが立ち上がっているか
http://0.0.0.0:8000/
にアクセスしてGeoJSONが返ってくるか- mapboxのトークンは記入されているか
だけ気をつけてもらえればあとはちゃんと表示されるはずです!
ブラウザで表示させてみる
ここまで出来たらあとは表示させるだけです!
$npm start
でサーバーを起動させてlocalhost:3000
に接続してみましょう!こんな感じで地図上にポイントが表示されて、ホバーすると情報が表示されるようになったと思います!!!
最後に
こんな感じで情報の収集・加工・配信・表示まで一連で行なってきましたが、データさえあれば簡単に可視化できるのが、最近のフレームワークの良いところですね!
また、データの加工段階でQGISなどのGISソフトなどを使えば都道府県ごとの人数をヒートマップで可視化などいろんなことができると思いますのでみなさんどんどんGISやっていきましょう!
- 投稿日:2020-04-02T04:27:22+09:00
JavaScriptのスコープについて
スコープとは
スコープとは変数を参照できる範囲のことです。今回はJavaScriptのvar,let,constについて解説します。
constは定数(再代入不可)の宣言なので特に難しく考えることはないのですが、letとvarは少し紛らわしいのでここを理解することでコードレビューの指摘が減ると思います。今回の記事用に書いたコード
コメントに解説が書いてあるので、これだけでも分かるかもしれませんが一応ひとつずつ解説します。
function Test(){ var hoge = 1 // hogeは関数スコープなのでTest関数の中で使う場合はエラーにならない console.log(hoge) if (true){ let fuga = 2 // fugaはブロックスコープなので、ブロック内(この場合はif文の中)で使用する場合はエラーにならない console.log(fuga) } // fugaはブロックスコープなので、ブロック内で定義した変数をブロックの外で呼び出そうとするとエラーになる // Uncaught ReferenceError: fuga is not defined console.log(fuga) } Test() // hogeは関数スコープなので、Test関数内で定義した変数を関数の外で呼び出そうとするとエラーになる // Uncaught ReferenceError: hoge is not defined console.log(hoge) // constで変数を定義すると定数扱いになるため、値の再代入はできない。 // Uncaught TypeError: Assignment to constant variable. const aaa = 3 aaa = 3var
関数内で有効となる変数。上記のコードでは変数hogeをvarで宣言している。Test関数の外でhogeを参照使用とするとエラーとなる
let
ブロック内で有効となる関数。上記のコードでは変数fugaをletで宣言している。変数を宣言したブロックの外でfugaを参照しようとするとエラーとなる。varに比べるとスコープが狭いため、想定外の値が入りにくいというメリットがある。
const
constで宣言された場合は定数となり、再代入は不可能となる。処理内容ごとに値が変わらないことを想定している場合は定数にしておく。
- 投稿日:2020-04-02T01:14:21+09:00
イテレータ・ジェネレータで構文解析
はじめに
@ikiuoさんが投稿された「パターン指定型パスワード ジェネレーター (JS版) - Qiita」を拝見したところ、イテレータとジェネレータでコンパクトに作れそうな気がしたので作ってみました。
構文パーサとして使えるように、「途中で戻したり進めたりできるイテレータ」も作りました。パターン構文
パターン構文のBNFはこんな感じでしょうか。
<syntax> ::= <item> | <item> <syntax> <item> ::= <char> | "%" <percent> <percent> ::= [<repeat>] ( <pattern> | "{" <brace> "}" | "[" <blacket> "]" | <char> ) <repeat> ::= ("0"|"1"|"2"|"3"|"4"|"5"|"6"|"7"|"8"|"9")+ <pattern> ::= <special> | <char> <special> ::= "b"|"o"|"d"|"X"|"x"|"A"|"C"|"c"|"B"|"V"|"v"|"D"|"Q"|"q"|"Y"|"Z"|"z"|"W"|"L"|"l" <brace> ::= <percent>+ <blacket> ::= <char>+パターン指定例
ソースコード
percent, brace, blacket をイテレータにし、イテレータが内容文字列を次々ジェネレートしてくれれば、ジェネレートされた文字列から乱数で1文字選んで次々文字列結合してパスワード文字列の完成、という目論みです。
ジェネレータはデータを読み出すと無くなってしまうので、イテレータが呼び出される度に文字列をジェネレートするジェネレータを作って返しています。function iter(pattern) { let index = 0 return { [Symbol.iterator]: function() { return this }, next: function() { if (index < pattern.length) { return { value: pattern[index++], done: false } } else { return { done: true } } }, fetch: function() { return pattern[index] }, skip: function() { index++ }, } } function charset(pattern, exclude = '') { function* chars() { let start = null const p = iter(pattern) for (let c of p) { if (c === '-' && start && p.fetch()) { const { value } = p.next() const end = value.charCodeAt(0) for (let code = start + 1; code <= end; code++) { yield String.fromCharCode(code) } start = null } else { if (c === '\\' && p.fetch()) { ( { value: c } = p.next() ) } yield c start = c.charCodeAt(0) } } } return [...chars()].filter(c => !exclude.includes(c)).join('') } function blacket(parser) { let chars = '' let in_escape = false let c = parser.next().value while (in_escape || c !== ']') { chars += c in_escape = !in_escape && c === '\\' c = parser.next().value } chars = charset(chars) return { [Symbol.iterator]: function* () { yield chars } } } function brace(parser) { const iterators = [] while (parser.fetch() !== '}') { iterators.push(percent(parser)) } parser.skip() return { [Symbol.iterator]: function* () { for (let iterator of iterators) { yield* iterator } } } } const SPECIAL = { b: '01', o: charset('0-7'), d: charset('0-9'), X: charset('0-9A-F'), x: charset('0-9a-f'), A: charset('A-Za-z'), C: charset('A-Z'), c: charset('a-z'), B: 'AEIOUaeiou', V: 'AEIOU', v: 'aeiou', D: charset('A-Za-z', 'AEIOUaeiou'), Q: charset('A-Z', 'AEIOU'), q: charset('a-z', 'aeiou'), Y: charset('0-9A-Za-z'), Z: charset('0-9A-Z'), z: charset('0-9a-z'), W: charset('0-9A-Za-z_'), L: charset('0-9A-Z_'), l: charset('0-9a-z_'), } const SUBMODE = { '[': blacket, '{': brace, } function percent(parser) { let repeat = 0 let c = parser.next().value while ('0' <= c && c <= '9') { repeat = repeat * 10 + parseInt(c) c = parser.next().value } repeat = repeat || 1 let iterator if (SUBMODE[c]) { iterator = SUBMODE[c](parser) } else if (SPECIAL[c]) { iterator = [SPECIAL[c]] } else { iterator = [c === '\\' && parser.next().value || c] } return { [Symbol.iterator]: function* () { for (let i = 0; i < repeat; i++) { yield* iterator } } } } function* parse(pattern) { let parser = iter(pattern) for (let c of parser) { if (c === '%') { yield* percent(parser) } else { yield c === '\\' && parser.next().value || c } } } function password(pattern) { return [...parse(pattern)].map(s => s[Math.random()*s.length|0]).join('') } console.log(password('qiita-%d%X%x%x%A%C%c%c%B%V%v%D%Q%q%Y%Z%z%W%L%l')) console.log(password('qiita-%3d')) console.log(password('qiita-%{dXxxACccBVvDQqYZzWLl}')) console.log(password('%8[01]')) console.log(password('%8[S-Z]')) console.log(password('%8[S\\-Z]')) console.log(password('qiita%4{-c3d}')) console.log(password('qiita%4{-Qvqd}')) console.log(password('qiita%2{-C3{2c[13579]}}'))実行結果
qiita-6C20VYymeUadTsKP4wTh qiita-465 qiita-3Ce0PMeeEAeXRjqZqEX0 10101101 VWWZTTUY ZS-ZSS-S qiita-o350-z239-g090-k848 qiita-Lup7-Pep9-Wif2-Jix2 qiita-Fnc9yp7zn7-Uru5ia9fe7
- 投稿日:2020-04-02T00:53:16+09:00
日本の航空路誌における座標表示をGoogle Mapsなどで使える10進法表示に置き換えるJavaScriptスニペット
国土交通省航空局が運営しているAIS Japanから入手できる航空路誌では、ウェイポイントやATC境界などの座標が以下のような形式で記載されています。区切りはありませんが、これは60進法表記です。
eAIP の
GEN 2.1.2
では、通常の書体で表記されているものは Tokyo Datum 、斜体で表記されているものはWGS84座標系であるとされています。適当にチャートを漁る限り、その多くが斜体(WGS84座標系)で示されているのでTokyo Datumはあまり考慮しなくてよいでしょう。ということで雑に「eAIPにおけるWGS84の60進法座標表示を、Google Maps などで使える10進法表示に変換する」スニペットを置いておきます。
なお、駐機場スポットの位置などで使われている小数点を含むものについては対応していません。function convert(aipCoord) { let aipLat = aipCoord.substring(0, 6); let aipLng = aipCoord.substring(7, 14); let latDeg = parseInt(aipLat.substring(0, 2)); let latMin = parseInt(aipLat.substring(2, 4)); let latSec = parseInt(aipLat.substring(4, 6)); let lngDeg = parseInt(aipLng.substring(0, 3)); let lngMin = parseInt(aipLng.substring(3, 5)); let lngSec = parseInt(aipLng.substring(5, 7)); let decimalLat = latDeg + latMin / 60 + latSec / 3600; let decimalLng = lngDeg + lngMin / 60 + lngSec / 3600; return { lat: decimalLat, lng: decimalLng }; }