- 投稿日:2020-12-01T23:59:13+09:00
月齢と月の満ち欠けを表示
JavaScriptで簡易的な月齢の計算及び月の満ち欠け表示を作成してみました。
日時及び平均朔望月からおおよその月齢を出し、月齢に応じた満ち欠けをcanvasを使用して描画しています。
満ち欠けの状態に応じた形をその都度描いているわけではなく、新月(暗 真円)、上弦及び下弦(明 半円)、リサイズ用真円(明暗切替)の3枚を重ね合わせて、CSSで反転や変形させることで表現しています。
言葉では伝わりにくいかと思いますが、こちらの満ち欠けテストページで適当に操作していただければ分かりやすいかもしれません。index.html<!doctype html> <html lang='ja'> <head> <meta name="viewport" content="user-scalable=no,width=device-width,initial-scale=1"> <meta charset='utf-8'> <title>月齢表示</title> <link rel='stylesheet' href='./style.css' type='text/css'> <script src='./script.js'></script> </head> <body> <div class='d'> <div class='moonAge'> <canvas id='a0'></canvas> <canvas id='a1'></canvas> <canvas id='a2'></canvas> </div> <div id='disp'> </div> </div> <input type='date' id='c' onchange='chg(this.value)'> </body> </html>style.css.d { background: black; width: 150px; } #disp { color: #ffff00; } .moonAge { position: relative; height: 200px; } canvas[id*='a'] { position: absolute; top: 0px; left: 0px; }script.js'use strict'; const size = 200; const topAngle = Math.PI + (Math.PI / 2) * 3; const bottomAngle = Math.PI + (Math.PI / 2); const pi2 = Math.PI * 2; const c = []; const ctx = []; const start = [0, topAngle, 0]; const end = [pi2, bottomAngle, pi2]; window.addEventListener('DOMContentLoaded', function(){ document.querySelector('.moonAge').style.height = size + 'px'; document.querySelector('.d').style.width = size + 'px'; for(let i = 0; i < 3; i++) { c[i] = document.getElementById('a' + i); c[i].style.width = size + 'px'; c[i].style.height = size + 'px'; c[i].width = size; c[i].height = size; ctx[i] = c[i].getContext('2d'); ctx[i].fillStyle = i === 0 ? '#444444' : '#ffff00'; ctx[i].arc(size / 2, size / 2, (size / 2) * .95, start[i], end[i]); ctx[i].fill(); } const e = document.querySelector('#c'); e.value = new Date().toLocaleDateString('sv'); chg(e.value); }, false); function chg(d){ const date = new Date(d), day = date.getTime() / 1000 / 86400 - 6.975, // 平均朔望月 r = 29.530588853 + 0.000000002162 * (new Date(date).getFullYear() - 2000), age = day > 0 ? day % r : r + day % r; document.querySelector('#disp').innerHTML = `${date.toLocaleDateString()}<br>月齢:${age.toFixed(1)}`; appearance(age, r); } function appearance(age, m = 29) { const s = Math.cos(pi2 * (age / m)), s2 = Math.sin(pi2 * (age / m)), r = Math.abs((size / 2) * s); ctx[2].clearRect(0, 0, size, size); ctx[2].fillStyle = s > 0 ? '#444444' : '#ffff00'; c[1].style.transform = 'rotate(' + (s2 > 0 ? 180 : 0) + 'deg)'; ctx[2].arc(size / 2, size / 2, (size / 2) * .95, 0, pi2); ctx[2].fill(); c[2].style.width = (r * 2) + 'px'; c[2].style.left = (size / 2 - r) + 'px'; }
- 投稿日:2020-12-01T23:53:11+09:00
12月なのでクリスマスソングにノるテンアゲなキーボードliteを実装!【WebHID API】 #iotlt
この記事はIoTLTアドベントカレンダー1日目です!(先に断っておきますが自作キーボード話ではないです) 先日のIoTLT vol69で話したネタをアップデートした感じです。
関係ないけどついに、この季節がやってきましたか〜 2020年めちIoTLT vol69 https://iotlt.connpass.com/event/192582/
テンション爆上がりなキーボードがこちら
クリスマスソングに合わせてキーボードを光らせる! #iotlt の #アドベントカレンダー2020 向けのやつです。 pic.twitter.com/pWqOULx8nX
— 菅原のびすけ (@n0bisuke) December 1, 2020毎年アドベントカレンダーネタでクリスマスっぽい話をやろうかと思ってたけど、全然やれてなかったので今回はクリスマス曲に合わせてみました。
記事の後半に実際に試せる(MacBookProでしか試してませんが)ページのURLを記載してますので是非手元でも試してみてください!
ちなみに再生してる曲はこちらから
なんとブラウザでキーボードバックライトの制御
Webブラウザからキーボードバックライトを制御出来ています。
ブラウザからハードウェアが制御できるAPIは色々と試して来てますが、今回は特に面白いですね。
WebHID API - Nintendo Switchも動かせるよ
HIDは
human interface devices
の略です。様々なヒューマンインターフェイスデバイスをJavaSciprtで制御出来るAPIです。参考記事から引用: ヒューマン インターフェース デバイス(HID)には、新しすぎる、古すぎる、あまりにも一般的でないなどの理由で、システムのデバイス ドライバーからアクセスできないロングテールなものがあります。WebHID API は、デバイス固有のロジックを JavaScript で実装する方法を提供することで、この問題を解決します。
ヒューマンインターフェイスデバイスと言われてもよく分からないと思いますが、 Nintendo Switchのコントローラー(Joy-Con)や冒頭のデモ動画のような キーボードのバックライトの制御が出来ます。
詳細は参考記事を読んでみてください。
利用はChrome v86以上かつ、現状オリジントライアル
WebHID APIは現時点だと、Google Chrome v86以上で対応しています。
他のブラウザの対応状況はCan I Useを見て確認しましょう。
2020/12/1現在 まだ全然対応してない......
https://caniuse.com/?search=webhid見てみると全然対応してないですね、最新版のChromeでもアップデートしただけでは使えず、オリジントライアルという利用するオリジン(利用するサイト的な)の登録作業をし、利用するユーザーのChromeのフラグ設定を変えてやっと利用出来ます。
Gamepad APIとの違いは?
Gamepad APIというものが既存で存在しましたが、Nintendo SwitchのJoy-Conが使えるとなると、このAPIとの違いは?と思う人もいるかもしれません。(この辺のハードウェア制御系のブラウザAPIをウォッチしてる人が少なさそうですが苦笑)
間違ってるかもですが僕が見ている印象だと、
Gamepad API
はインプットのみでWebHID API
の方がやれることは多そうな印象でした。
- Gamepad API
デバイス -> ブラウザ
のインプットのみ- ゲームパッドのみ
- WebHID API
デバイス <-> ブラウザ
でインプットとアウトプット両方- ゲームパッド以外も対応
上位互換というよりかは、Gamepad APIはゲームパッドからのインプットに特化してる気がします。ちゃんと調べ切れてないので、認識間違ってたらコメント下さい。
WebHID APIを利用してみよう
では実際にWebHID APIを利用してみましょう。
事前準備
最初にちょっと準備が必要です。
クライアント(ブラウザ)側
フラグを確認する必要があります。
chrome://flags/
にアドレスバーからアクセスしま、#experimental-web-platform-features
のフラグをenable
にしましょう。サーバー側、オリジンの申請
WebHID APIは、現状オリジントライアルというステータスで、利用するオリジンをGoogleに申請する必要があります。
こちらからオリジントライアル申請します。
ドメインだけでなく、オリジンなのでちゃんと
https://
も入れましょう。
- パスは不要です。
- 例えば
https://hogehoge.com/huga/index.html
で利用したい場合はhttps://hogehoge.com
を登録すれば大丈夫です。- SSLがマストです。
http
は使えません、https
がマストです。無事に登録できると次のページでトークンが発行されます。
利用するページのHTMLファイルのmetaタグに以下のような指定をし、発行されたトークンを指定することでWebHID APIが利用出来ます。
<meta http-equiv="origin-trial" content="TOKEN_GOES_HERE">補足: npxで気軽にサーバー起動とトンネリング
オリジンの登録ということは、基本ホスティング環境ということになりますが、毎回デプロイしなおすのは手間なので、最近はserveとngrokの二つのコマンドをnpx経由でインストールせずに実行して手軽に試してます。
- ローカルサーバー起動
$ npx serve -l 4444
- トンネリングサーバー起動
$ npx ngrok http 4444
ngrokで発行されたオリジンをオリジントライアルに登録するとデプロイなどせずに手軽に試せます
コードの実装
index.html
とscript.js
を作成しました。HTML側 - トークンの指定
先ほども紹介したように、
index.html
のhead内に以下を記載しましょう。<meta http-equiv="origin-trial" content="TOKEN_GOES_HERE">TOKEN_GOES_HEREの箇所は登録して発行されたトークンです。
JavaScript側 - navigator.hid
script.js
は以下のコードで実行してみます。利用できる状態だと、
navigator.hid
が利用出来ます。if ("hid" in navigator) { // The WebHID API is supported. console.log(`HIDが使えるよ`); } else { console.log(`HIDが使えません`); }コンソールに
HIDが使えるよ
と表示されればOKです。
https://~~
で登録してるのにブラウザで確認するときにhttp://~~
になってるなど登録した条件と違う場合は動作しないので注意しましょう。Nintendo Switchのコントローラーで試す
そのまま実行すると怒られるのでボタンを追加して、ボタンクリックからスタートするようにします。
Uncaught (in promise) DOMException: Failed to execute 'requestDevice' on 'HID': Must be handling a user gesture to show a permission request.
web.devのサンプルコードからそのままという感じですが、Nintendo SwitchのJoy-Conは
vendorId
やproductId
が↓のような値なので指定しましょう。// Filter on devices with the Nintendo Switch Joy-Con USB Vendor/Product IDs. const filters = [ { vendorId: 0x057e, // Nintendo Co., Ltd productId: 0x2006 // Joy-Con Left }, { vendorId: 0x057e, // Nintendo Co., Ltd productId: 0x2007 // Joy-Con Right } ]; const main = async () => { // Prompt user to select a Joy-Con device. const [device] = await navigator.hid.requestDevice({ filters }); }; if ("hid" in navigator) { // The WebHID API is supported. console.log(`HIDが使えるよ`); const btn = document.querySelector(`button`); btn.addEventListener('click', main); }else{ console.log(`HIDが使えません`); }Webサイトにアクセスし、スタートボタンを押すと接続済みのJoy-ConがHIDとして認識されます。
また、このような実装まで繋げてあげると、 Joy-Conのバイブレーション機能を利用出来ます。
// First, send a command to enable vibration. // Magical bytes come from https://github.com/mzyy94/joycon-toolweb const enableVibrationData = [1, 0, 1, 64, 64, 0, 1, 64, 64, 0x48, 0x01]; await device.sendReport(0x01, new Uint8Array(enableVibrationData)); // Then, send a command to make the Joy-Con device rumble. // Actual bytes are available in the sample below. const rumbleData = [ /* ... */ ]; await device.sendReport(0x10, new Uint8Array(rumbleData));
device.sendReport()
でデータ配列を送ってバイブを制御しています。ここのデータを変えて試してみたいですね。
キーボードバックライトで試す
同じようなアクセスの仕方で、キーボードのバックライトをJavaScriptから制御できます。
vendorIdなどのパラメータも参考記事を元に指定します。
const [device] = await navigator.hid.requestDevice({ filters: [{ vendorId: 0x05ac, usage: 0x0f, usagePage: 0xff00 }] });リクエストすると
Keyboard Backlight
の許可を求められます。// Blink! const reportId = 1; for (let i = 0; i < 10; i++) { // Turn off await device.sendFeatureReport(reportId, Uint32Array.from([0, 0])); await waitFor(100); // Turn on await device.sendFeatureReport(reportId, Uint32Array.from([512, 0])); await waitFor(100); }音楽に合わせてキーボード光らせるのはこちらで試せます
最後ですが、ここまで試して、キーボードを光らせることが出来たので、音楽にノらせてパリピ感のある演出をしてみたいなと突発的に思いました。これが最初のデモになります。
Izm Logさんのブログを参考に、ビート検出のコードを利用させてもらい、 ビート検出時にキーボードを光らせる というプログラムを作ってみましたが、けっこういい感じの反応になりました。
デモ動画の最後にも載せてますが、指パッチンでの反応もいい感じです。
MacBookProでしか試せてませんが、こちら↓で試せます。
- マイクの利用を許可する
- リクエストのボタンを押す
- デバイス利用の許可を求められるので、
Keyboard Backlight
を選択
- (MacBookPro以外の人はここでデバイスが表示されないかも)
- オープンのボタンを押す
- このページを開きながら、何か音楽を流す
- この時、別タブでYoutubeを長そうとすると、このページの処理が止まってしまうので、一つのPC内で試す際は別タブではなく
別ウィンドウ
で開くようにしましょう。このページがアクティブになってる状態にして下さい。まとめ
色々と書きましたが、Chrome v86から利用できるようになったWebHID APIを利用してみたという記事です。
まだGoogle公式のサンプルを少しいじったくらいしか試せてませんが、キーボードのバックライトをいじれるのはけっこう楽しいです。
Nintendo Switchのコントローラーもまだまだ遊びきれてないのでまた少し触ってみたいなぁ
前回のIoTLT vol69の時の様子はこちらのYoutubeでも観れるのでぜひご覧ください!そしてチャンネル登録も是非!
次回は12/15にIoTLTを実施しますのでこちらもよろしくお願いします!
明日は@3yaka4さんによる
ねこIoT
的な何かの記事です!
楽しみですね〜 :)少し早いですが、今年もありがとうございました!来年もよろしくお願いします!
それでは!
- 投稿日:2020-12-01T23:25:45+09:00
地理院タイルとdeck.glで3D地図をつくろう
この記事はMIERUNEアドベントカレンダー2日目の記事です!
1日目は最近弊社にジョインした@igarashiさんがFOSS4GHokkaidoでの登壇から引き続き、とてもアツい記事を投稿されましたのでMIERUNE的なインパクトは十分、2日目はゆるふわに富士山でも見て涼んでいきましょう。
TL;DR
国土地理院の標高タイルで3D表現をするためのGsiTerrainLayerをつくりました。
https://github.com/Kanahiro/deckgl-gsi-terrain-layernpmモジュールとして公開済みです、以下でインストール出来ます。
npm install deckgl-gsi-terrain-layerはじめに
QGISやウェブ問わず、GISの使い始めは、まずはOSM地図や地理院タイルを表示するところから始める方が多いのではないでしょうか。今回は地理院タイルにフォーカスしていく訳ですが、国土地理院は空中写真だけではなく、様々なデータをラスタータイルとして配信しています。
https://maps.gsi.go.jp/development/ichiran.html
こちらがその一覧です。ベースマップなどは定番ですね。この中に標高タイルなるデータがあります。標高データ、DEMと言えば標高値をグレースケール画像に変換した白黒のアレをイメージしますが、標高タイルでは何やらカラフルな画像が配信されているようです(下記画像)。
※地理院タイルより10mDEM、東京都と富士山のまわり10mレベルのデータが既に配信されていて無料で使える訳ですから、これを用いてウェブ地図で3D表示出来たら便利ですよね。ということで色々やったのが本記事です。
RGB値の標高換算
先ほども言ったとおりDEMと言えばグレイスケールですが、標高値をRGB画像に変換するというのは、標高タイル以外にもやっている例があります。
https://docs.mapbox.com/help/troubleshooting/access-elevation-data/
たとえばMapbox GL JSではterrain-RGBとして、以下の計算式によりRGB値を標高値に換算しています(もともとはMapzenという会社が定義した規格のよう)。height = -10000 + ((R * 256 * 256 + G * 256 + B) * 0.1)何やら難しく見えますが、RGBそれぞれが0-255の値をとるので、RGB値を3桁の256進数として見立ててRGBと標高をやり取りしている訳です(傾き0.1の単調増加関数と言えますね)。
先ほどの標高タイルと同じエリアの標高を、この式から得られるterrain-RGB画像にすると以下のような画像になります。見た目がずいぶん違う事がわかりますね。
地理院の標高タイルも、ベースの考え方は全く同じで、計算式が異なり以下です(仕様を解釈して式になおしています)。
height = (R * 256 * 256 + G * 256 + B) * 0.01 # ただし[R,G,B] = [128,0,0]を無効値とする # ただし[128,0,1]以上の場合2^24を引き去るここでサラッと書いた但し書きがミソで、これらのせいで、RGB値→標高値の関係が、単調増加と言えなくなっており、Mapboxなどが採用するterrain-RGBの仕組みに乗っかれなくなってしまっています。
Mapzen Terrain-RGBと地理院標高タイルの考え方の違い
(こきたない落書きですみません…)
※とくに標高タイルの方、間違いがありましたらご指摘くださいそれでも標高タイルを使いたい理由
既にホスティングされていて、自由に使えることに尽きます。
terrain-RGBを配信しているサービスはいくつかありますが、API-keyが必要だったり、アクセス数がそれなりにあれば当然費用が発生したりと、ライトに使えるものはありません。
また、標高タイルの元データである10mDEMは、基盤地図情報から取得可能なので自前で変換してホスティングする事も不可能ではありませんが、それを全国分やるのは骨です。標高タイルをdeck.glで3D表示してみた
Mapbox GL JSでは陰影図は表示出来ますが3D表示は出来ません。terrain-RGBの表示には、deck.glのTerrainLayerが良い感じです。deck.glのTerrainLayerをベースに、標高タイルに準拠するよう拡張したGsiTerrainLayerをつくってみました。
ちなみにTerrainLayerでは任意の標高画像に対応していて、グレースケール画像でも標高表現が可能ですが、RGB値と標高値の関係が単調増加である必要があり、標高タイルはどうやっても読ませる事ができません。
サンプルページ
※羊蹄山deckgl-gsi-terrain-layer
で、GsiTerrainLayerを使うためのモジュールはnpmで既に公開済みです。
https://github.com/Kanahiro/deckgl-gsi-terrain-layernpm install deckgl-gsi-terrain-layer
これでインストール可能です。詳細な使い方は本記事では割愛します、上記リポジトリのREADMEを参照してください。
終わりに
いかがだったでしょうか?
グレースケールPNGでは分解能は16bitが最大でしょうか。その点、24bitの分解能を持つRGB-PNGが標高表現で有利なのは明らかでしょう。本記事をきっかけにterrain-RGBの利用が少しでも広がったらうれしいですね。
ちなみに、過去にMapbox GL JSで同じ様な事をされた方がいらっしゃったみたいですね。そんな訳でMIERUNEアドベントカレンダー2日目でした。3日目は@mits003さんです、ぜひご覧ください:)
- 投稿日:2020-12-01T23:00:35+09:00
Vue.jsで超簡単なタイピングゲームを作ります
完成品
App.vue<template> <div id="app"> <div v-if="playing"> <span>{{ pressed }}</span>{{ word }} <br> <br> miss:{{ miss }} </div> <div v-else>Spaceでスタート</div> </div> </template> <script> export default { data() { return { words: ['apple', 'banana', 'grape'], word: '', pressed: '', miss: 0, playing: false, }; }, created() { addEventListener('keydown', (e) => { if (e.key !== ' ' || this.playing) { return; } this.playing = true; this.setWord(); this.keyDown(); }); }, methods: { setWord() { this.word = this.words.splice(Math.floor(Math.random() * this.words.length), 1)[0]; }, keyDown() { addEventListener('keydown', (e) => { if (e.key !== this.word[0]) { this.miss++; return; } this.pressed += e.key; this.word = this.word.slice(1); if (this.word.length === 0) { this.pressed = ''; if (this.words.length === 0) { this.word = 'おしまい'; return; } this.setWord(); } }); }, }, }; </script> <style> #app { font-family: Avenir, Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; margin-top: 60px; } span { opacity: 0.5; } </style>配列からランダムな値を取り出す
setWord
メソッドを作り、created
で呼び出しています。
その際words
の中からランダムな値をword
に格納。App.vue<template> <div id="app"> <div> {{ word }} </div> </div> </template> <script> export default { data() { return { words: ['apple', 'banana', 'grape'], word: '', } }, created() { this.setWord(); }, methods: { setWord() { this.word = this.words[Math.floor(Math.random() * this.words.length)]; } } } </script> <style> #app { font-family: Avenir, Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; margin-top: 60px; }タイピングの処理
keyDown
メソッドを作り、created
で呼び出しています。
入力されたKeyと現在のword
の頭文字が一致しない場合はmiss
が加算。
pressed
には入力されたkeyを格納。
word
にはkeyが押されるごとに頭文字を削除し再代入します。
これで入力した文字と未入力の文字を仕分けしています。
word
が全て無くなったらpressed
をリセットし、新しいword
を表示させます。App.vue<script> export default { data() { return { words: ['apple', 'banana', 'grape'], word: '', pressed: '', miss: 0, } }, created() { this.setWord(); this.keyDown() }, methods: { setWord() { this.word = this.words[Math.floor(Math.random() * this.words.length)]; }, keyDown() { addEventListener('keydown', (e) => { if (e.key !== this.word[0]) { this.miss++; return; } this.pressed += e.key; this.word = this.word.slice(1); if (this.word.length === 0) { this.pressed = ''; this.setWord(); } }); }, } } </script>
Viewで
pressed
とword
連結させます。
入力されていることが分かりやすいようにpressed
をspan
タグで囲みスタイルをつけます。あと
miss
のカウントをここで表示させます。App.vue<template> <div id="app"> <div> <span>{{ pressed }}</span>{{ word }} <br> <br> miss:{{ miss }} </div> </div> </template> <style> span { opacity: 0.5; } </style>
単語の重複を防ぐ
現時点だと単語が重複して無限に遊べるので一度出た単語は消してしまいます。
App.vue<script> setWord() { this.word = this.words.splice(Math.floor(Math.random() * this.words.length), 1)[0]; }, </script>ゲームの開始と終了の処理
data
にplaying
を追加しSpaceキーを押すことでfalse
からtrue
にします。App.vue<template> <div id="app"> <div v-if="playing"> <span>{{ pressed }}</span>{{ word }} <br> <br> miss:{{ miss }} </div> <div v-else>Spaceでスタート</div> </div> </template> <script> export default { data() { return { words: ['apple', 'banana', 'grape'], word: '', pressed: '', miss: 0, playing: false, } </script>
Spaceキーが押されたら
playing
がtrue
になり、ゲームスタートです。
|| this.playing
で再スタートを防ぎます。App.vue<script> created() { addEventListener('keydown', (e) => { if (e.key !== ' ' || this.playing) { return; } this.playing = true; this.setWord(); this.keyDown(); }); }, </script>
words
が空になったらおしまい
と表示させゲーム終了です。App.vue<script> keyDown() { addEventListener('keydown', (e) => { if (e.key !== this.word[0]) { this.miss++; return; } this.pressed += e.key; this.word = this.word.slice(1); if (this.word.length === 0) { this.pressed = ''; if (this.words.length === 0) { this.word = 'おしまい'; return; } this.setWord(); } }); }, } } </script>おしまい
以上となります。
もっとこうした方が良いんじゃない?
というのがあれば是非教えて下さいm(_ _)m
- 投稿日:2020-12-01T22:53:36+09:00
PythonでJavaScriptの分割代入のようなことをする
やりたいこと
JavaScriptの分割代入のようなことがPythonでしたいです。
destructuringAssignment.jslet a, b, c; [a, b, c] = ["x", "y", "z"]; console.log(a, b, c); // x y z結論
Pythonのシーケンスのアンパックによって実現できます。
unpack.pya, b, c = ("x", "y", "z") print(a, b, c) # x y z余談
Pythonのテキストデータはテキストシーケンス型であり、シーケンスのアンパックが行えます。
textunpack.pya, b, c = "xyz" print(a, b, c) # x y zこのPythonの挙動を見て、JavaScriptではどうなるのか試したところ。。。
textDestructuringAssignment.jslet a, b, c; [a, b, c] = "xyz"; console.log(a, b, c); // x y zいけました!ただし、色々調べましたがこちらの仕様がECMAScriptのどこで定義されているのかはよくわかりませんでした。。。
さらにここから、見かけ上Pythonと同様の記法も試してみました!
textUnpack.jslet a, b, c; a, b, c = "xyz"; console.log(a, b, c); // undefined undefined xyzどうやら左辺の最後の変数に右辺の値が代入され、左辺の最後以外の変数は
undefined
となるようです。
このあたりの仕様も一旦この記事にメモ書きして、興味が湧いたらまた調べます!参考URL
分割代入 - JavaScript | MDN
5. データ構造 — Python 3.9.1rc1 ドキュメント
組み込み型 — Python 3.9.1rc1 ドキュメント
- 投稿日:2020-12-01T22:50:57+09:00
異業種から転職して感じたwebエンジニアに必要な素養
とりあえず箇条書き
・分かりづらいことを分かりやすく説明する能力
・急な内容修正などに対する対応力と柔軟性
・チームの人に対する思いやりと気遣い(コードの工夫やコミュニケーションなどで)
・自分の書くコードに過度な愛着やこだわりを持たない
・自分の思い通りにいかなくても許容する広い心
・ファイル内容やDB構造など広く記憶する記憶力
・どんなバグにもへこたれない強いメンタル
・行き詰ったらすぐに質問をする
・手遅れになる前に相談する
・小まめに進捗報告をする(実は気付いていない内容がよくある)
・どんな状況においてもポジティブシンキング
・自分の間違いを認められること
・相手の間違いを許せること
・意見が合わなくてもレスバしないでうまく妥協点を見つける、その場を丸く収める
・納期を守る、約束を守る
・ダブルチェック、確認をしっかりとする
・あいさつをする
・遅刻しない
・整理整頓
・清潔感
・笑顔もうエンジニア関係なくなっちゃった!
今日の名言
If today were the last day of my life, would I want to do what I am about to do today?
(もし今日が人生最後の日だとしたら、今しようとしていることが、本当にしたいことだろうか?)-スティーブ・ジョブズ
- 投稿日:2020-12-01T22:12:10+09:00
配列の重複するオブジェクトを取り除きつつ数える unique関数 group 関数 の実装
この記事は、下記記事のメント欄に書こうと思ったのですが、長すぎるコードになったので単独記事にしました。
配列の重複するオブジェクトを取り除きつつ数えたい - Qiita
https://qiita.com/torajiro1220/items/caac851ae3a3c4eb7fad上記記事のデータ加工の1つめのものだけ対象です。
const data = [ {name: 'aaa', age: 18,}, {name: 'bbb', age: 20,}, {name: 'bbb', age: 21,}, {name: 'ccc', age: 21,}, {name: 'bbb', age: 20,}, ];上記のものを、下記のように加工したいということになります。
[ { name: 'aaa', age: 18, count: 1 }, { name: 'bbb', age: 20, count: 2 }, { name: 'bbb', age: 21, count: 1 }, { name: 'ccc', age: 21, count: 1 }, ],元記事では reduce の例が載っています。
私は、reduceはあまり可読性がよくないと思っているので使わないようにしています。紹介するユニーク化やグループ化できる関数を作っておき再利用したりすると便利かと思います。
unique関数
こんな感じの unique 関数作っています。外部関数で動作制御でき、detailフラグで結果だけを得るか詳細情報を得るかを分岐させています。
const unique = ( array, func = v => v, detail = false, ) => { const index = []; const result = []; const count = []; array.forEach(v => { const funcResult = func(v); const indexResult = index.indexOf(funcResult); if (indexResult === -1) { index.push(funcResult); result.push(v); count.push(1); } else { count[indexResult] += 1; } }); if (detail) { return { index, result, count }; } return result; }; const data = [ {name: 'aaa', age: 18,}, {name: 'bbb', age: 20,}, {name: 'bbb', age: 21,}, {name: 'ccc', age: 21,}, {name: 'bbb', age: 20,}, ]; console.log( unique(data, d => d.name, {detail: true}), ); // { // index: ['aaa', 'bbb', 'ccc'], // result: [ // { name: 'aaa', age: 18 }, // { name: 'bbb', age: 20 }, // { name: 'ccc', age: 21 } // ], // count: [1, 3, 1] // } var result = unique(data, d => d.name, {detail: true}); console.log( result.result.map((e, i) => ({ name: e.name, count: result.count[i]})), ); // [ // { name: 'aaa', count: 1 }, // { name: 'bbb', count: 3 }, // { name: 'ccc', count: 1 }, // ], console.log( unique(data, d => d.name + d.age.toString(), {detail: true}), ); // { // index: ['aaa18', 'bbb20', 'bbb21', 'ccc21'], // result: [ // { name: 'aaa', age: 18 }, // { name: 'bbb', age: 20 }, // { name: 'bbb', age: 21 }, // { name: 'ccc', age: 21 } // ], // count: [1, 2, 1, 1] // } var result = unique( data, d => d.name + d.age.toString(), {detail: true} ); console.log( result.result.map((e, i) => ({ name: e.name, age: e.age, count: result.count[i] })), ); // [ // { name: 'aaa', age: 18, count: 1 }, // { name: 'bbb', age: 20, count: 2 }, // { name: 'bbb', age: 21, count: 1 }, // { name: 'ccc', age: 21, count: 1 }, // ],こちらで動作確認可能です。
https://jsbin.com/vazijipiqe/edit?html,js,consolegroup関数
また、このようなgroup関数を使っても同じデータ加工ができます。
const group = ( array, func = v => v, detail = false ) => { const index = []; const result = []; array.forEach(v => { const funcResult = func(v); const i = index.indexOf(funcResult); if (i === -1) { index.push(funcResult); result.push([v]); } else { result[i].push(v); } }); if (detail) { return { index, result }; } return result; }; const data = [ {name: 'aaa', age: 18,}, {name: 'bbb', age: 20,}, {name: 'bbb', age: 21,}, {name: 'ccc', age: 21,}, {name: 'bbb', age: 20,}, ]; console.log( group(data, d => d.name, {detail: true}), ); // { // index: ['aaa', 'bbb', 'ccc'], // result: [ // [{ name: 'aaa', age: 18 }], // [ // { name: 'bbb', age: 20 }, // { name: 'bbb', age: 21 }, // { name: 'bbb', age: 20 }, // ], // [{ name: 'ccc', age: 21 }] // ] // } console.log( group(data, d => d.name, {detail: true}) .result.map(e => ({ name: e[0].name, count: e.length })), ); // [ // { name: 'aaa', count: 1 }, // { name: 'bbb', count: 3 }, // { name: 'ccc', count: 1 }, // ] console.log( group(data, d => d.name + d.age.toString(), {detail: true}), ); // { // index: ['aaa18', 'bbb20', 'bbb21', 'ccc21'], // result: [ // [{ name: 'aaa', age: 18 }], // [ // { name: 'bbb', age: 20 }, // { name: 'bbb', age: 20 }, // ], // [ // { name: 'bbb', age: 21 }, // ], // [{ name: 'ccc', age: 21 }], // ], // } console.log( group(data, d => d.name + d.age.toString(), {detail: true}) .result.map(e => ({ name: e[0].name, age: e[0].age, count: e.length })), ); // [ // { name: 'aaa', age: 18, count: 1 }, // { name: 'bbb', age: 20, count: 2 }, // { name: 'bbb', age: 21, count: 1 }, // { name: 'ccc', age: 21, count: 1 }, // ],こちらで動作確認可能です。
https://jsbin.com/zikeniteva/edit?html,js,consoleまとめ
unique と group は、用途が違うものですが、汎用的に使えるのでこのようなデータ加工を簡単に行うことができました。
unique も group も、自作ライブラリの Parts.js に搭載しています。プロジェクトの開発を楽にしたい方向けの便利関数を多く用意しているので、よかったら使ってみてください。マニュアル作れて無いのでテストコードみないと動きがわからないので、結構上級者用かもですが、直感的に理解できる感じの動作をする関数群を用意しています。
関数実装のサンプルなどや、WebPackのビルド設定参考などにも、どうぞです。
standard-software/partsjs
https://github.com/standard-software/partsjs
- 投稿日:2020-12-01T22:03:37+09:00
いまこそ知ってほしい!JavaScriptのconsoleの魅力 2020
こんにちは!
ウェブクルー Advent Calendar 2020 の3日目の記事です。
昨日は @kouchanne さんの「Anyダメ絶対! axios編」でした!
TypeScriptでAPIからのresponseデータにも型をセットしたいときに参考にしたいですね。アドベントカレンダーのお祭り気分を楽しみながら書いていきます!
JavaScriptのconsoleオブジェクトのメソッド
普段使うわりに意外と検索する対象に入らないのではないでしょうか?
知ってみたら、面白いところも便利なところもあるなぁーって感じました。
いろいろ試したことを書いていきます。JavaScriptのconsole.log()でフォーマット
CSSのフォーマット
ログを出力するのに使う
console.log()
の出力をCSSでフォーマットできるようです。
強調したいログに適用できて便利ですね。試してみましょう。
const criminal = "御主人" console.log(`犯人は・・・%c${criminal} %c、あなたです!!!`, "color: red;", "");
第一引数でCSSを適用したい文字の前に%c
ディレクティブをつけて、
第二引数以降で宣言したCSSを適用させることができましたね。console.log()は画像も出せる(Chromiumのブラウザのみ)
CSSで画像設定してもコンソールに出力できるようで、Chromeでちょっと試してみました。
console.log("%cHAPPY HOLIDAYS!%c ", 'color: #FFFFFF; font-size:64px; font-family:"Georgia"; background: -webkit-linear-gradient(0deg, #00BB00, #FFA500, #FF0000); padding: 8px;', 'background: url(https://1.bp.blogspot.com/-xRE8fggFEAs/X8BV4cGFpYI/AAAAAAABcgE/sgoe7drgu0kgTkUweELLBeGZK0tT7osKgCNcBGAsYHQ/s1103/christmas_mask_santa_tonakai.png); background-size: 100% 100%; padding: 256px 256px');画像は以下の「いらすとや」さんのものを使っています。
マスクを付けたサンタとトナカイのイラスト(クリスマス) | かわいいフリー素材集 いらすとやconsole.log()のCSSのブラウザでの違い
console.log()
のCSSですが、ブラウザによって挙動が違いました。
上記のはAppleのサイトっぽく、文字をくりぬいた形にしたかったのですが、
Chromeだと-webkit-background-clip: text;
を適用できなかったですね。cssに-webkit-background-clip使ったconsole.log()console.log("%cHAPPY HOLIDAYS!", 'color: #FFFFFF; font-size:64px; font-family:"Georgia"; background: -webkit-linear-gradient(0deg, #00BB00, #FFA500, #FF0000); -webkit-background-clip: text; color: transparent; padding: 8px;');逆に、Firefoxだと画像が出せなかったです。
なのでサンタさんの画像を出力するところではChromeで実行したGIF画像を貼りました。CSSは以下を参考:
CSS3でテキストにグラデーションをかける方法
[CSS]background-clipで文字の形に背景を切り取る
background-clip - CSS: カスケーディングスタイルシート | MDNいろいろなフォーマット
他にも以下のようなフォーマットがあります!
置換文字列 変換用途 備考 %s
文字列 %d
または%i
整数 %f
浮動小数点 ※Chromeだと動作しない %o
または%O
オブジェクト これは「console - Web API | MDN」や「Console Standard #formatting-specifiers」に書いてある通りで、
全部を一気にFirefoxで試してみるとこんな感じですね。console.log("%s, %.3f, %i, %o", "a", 1.1, 10,{id: 1});JavaScriptのconsoleのメソッドをログレベル別で出力
info()、warn()、error()
ログレベルを変えて出力することも出来ます。
Chromeでの実行結果です。console.log("log()"); console.info("info()"); console.warn("warn()"); console.error("error()");「
log()
とinfo()
同じじゃん」って感じなんですが、
次はFirefoxでの実行結果です。
info()
の出力にはlog()
の出力にはない「ⓘ」が出てきました!
Firefoxでなら差が出るようです。
warn()
やerror()
は、コンソール開くとよく見かけるやつですね。これらのほかに
debug()
というメソッドもあって、
ChromeだとログレベルがVerboseで、Firefoxだとログレベルがデバッグです。assert()
error()
を条件によって出しわけたいなって時に役立つと思います。console.assert(true, "エラーは出ない"); console.assert(false, "エラーが出る!");
assert()
だと、error()
に出なかった「Assertion failed: 」がエラーメッセージと一緒に出るようですね。info()、warn()、error()、assert()もフォーマット可能
Firefoxで試してみた実行結果です。
設定できる置換文字列を全部設定してます(CSSはFirefoxっぽく色を変えてみました笑)。console.info('%c %s, %.3f, %i, %o', 'color: #FFFFFF; font-size:64px; font-family:"Georgia"; background: -webkit-linear-gradient(0deg, #9758F5, #E9207A, #FF8552); -webkit-background-clip: text; color: transparent; padding: 8px;', 'a', 1.1, 10,{id: 1} );
こんな感じで、置換文字列がすべてフォーマットされたものが出力されました。log()、info()、warn()、error()、assert()のグループ分け
「Console Standard #loglevel-severity」に書いてあるのですが、
一般的なグループ分けは以下ですね。
グループ分け メソッド 説明 log log() 一般的なログ info info() 参考になるログ warn warn() ユーザーになにか警告するログ error error(), assert() ユーザーにエラーを示すログ console.time()で時間を計測
time()
console.time()
でタイマーを開始し、
console.timeLog()
で途中経過時間を出力、
console.timeEnd()
でタイマー終了とかかった時間を出力できます。測定するときに使えそうですね。
time()で測定してみる
試すのにちょうどいいの探して、フィボナッチ数列求める再帰処理がちょうどいいかもと思って
以下の中で使われていたのを参考にさせてもらっています。
Haskellをかける少女
Haskellをかけない中年(「昨日のコード」)
const fibo = n => (n === 0 || n === 1)? n: fibo(n - 1) + fibo(n - 2);(「今のコード」)
const rec = (f2, f1, n) => { if (n === 1) return f1; return rec(f1, f2+f1, n-1); }; const fibo = n => (n < 2)? n: rec(0, 1, n);ハスケル子「昨日のコードだと42番目のフィボナッチ数を求めたくらいでSafariが悲鳴を上げ始めますけど、」
ハスケル子「今のコードなら、1000番目のフィボナッチ数が」
ハスケル子「1ミリ秒以下で返ってきます」どのくらい違うのか、
time()
を使ってちょっと測ってみました!
上記の「昨日のコード」の処理をChromeで計測してみたものです。const fibo = n => (n === 0 || n === 1)? n: fibo(n - 1) + fibo(n - 2); console.time("MyTimer"); console.log(fibo(30)); console.timeLog("MyTimer"); console.log(fibo(35)); console.timeLog("MyTimer"); console.log(fibo(40)); console.timeEnd("MyTimer");
40くらいまで行くと時間がかかっているのが数字に表れていますね。次に、上記の「今のコード」の処理をChromeで計測してみたものです。
const rec = (f2, f1, n) => { if (n === 1) return f1; return rec(f1, f2+f1, n-1); }; const fibo = n => (n < 2)? n: rec(0, 1, n); console.time("MyTimer"); console.log(fibo(30)); console.timeLog("MyTimer"); console.log(fibo(35)); console.timeLog("MyTimer"); console.log(fibo(40)); console.timeLog("MyTimer"); console.log(fibo(1000)); console.timeEnd("MyTimer");
今のChromeは末尾呼出しの最適化されているようで、
他の処理の後で1000番目まで計算しても余裕で1ミリ秒以下で返ってきてるのがわかりますね。time()で複数のタイマー
time()
は第一引数に文字列や数字やオブジェクトをラベルとして入れて、
複数のタイマーを用意することもできます。timelog()はフォーマット可能?
通常の
console.log()
のように複数の引数をとることはできます。
第一引数に%c
の文字を入れた文字列をラベルとして設定して、Chromeで試してみましょう。const time1 = {label: "%ctimer_1", color: "font-size: 16px; color: red;"}; const time2 = {label: "%ctimer_2", color: "font-size: 16px; color: blue;"}; console.time(time1.label, time1.color, "1スタート", "出力されないです"); console.timeLog(time1.label, time1.color); console.time(time2.label, time2.color, "2スタート", "出力されないです"); console.timeLog(time2.label, time2.color); console.timeLog(time1.label, time1.color); console.timeEnd(time1.label, time1.color, "1エンド", "出力されないです"); console.timeLog(time2.label, time2.color); console.timeEnd(time2.label, time2.color, "2エンド", "出力されないです");
複数のタイマーを設定したときに、CSSを適用させるのというのは使えるかもしれないですね。ところで、CSSがラベルだけじゃなくて経過時間にも適用されてる感じですね?
経過時間には別のCSSを適用できそうですね。
こちらもChromeで試してみましょう。const timerLabel = "%ctimer%c"; const color_2e2623 = "font-size:32px; color: #fff; text-shadow: 1px 1px 1px #2e2623, -1px 1px 1px #2e2623, 1px -1px 1px #2e2623, -1px -1px 1px #2e2623, 1px 1px 1px #2e2623, -1px 1px 1px #2e2623, 1px -1px 1px #2e2623, -1px -1px 1px #2e2623; background: #332826; padding: 16px 4px;" const color_53b793 = "font-size:32px; color: #fff; text-shadow: 1px 1px 1px #53b793, -1px 1px 1px #53b793, 1px -1px 1px #53b793, -1px -1px 1px #53b793, 1px 1px 1px #53b793, -1px 1px 1px #53b793, 1px -1px 1px #53b793, -1px -1px 1px #53b793; background: #47b58e; padding: 16px 4px;" console.time(timerLabel); console.timeLog(timerLabel, color_53b793, color_2e2623); console.timeLog(timerLabel, color_2e2623 + "text-transform: uppercase;", color_53b793); console.timeLog(timerLabel, color_53b793, color_2e2623 + "text-transform: uppercase;"); console.timeLog(timerLabel, color_2e2623 + "text-transform: uppercase;", color_53b793 + "text-transform: uppercase;"); console.timeEnd(timerLabel);ラベルと別で経過時間にCSSでフォーマットすることができましたね。
ただ、FirefoxだとCSS適用できなかったです。CSSは以下を参考:
HTMLとCSSで作れる!見出しに使えるおしゃれな文字装飾サンプル | CJコラムconsole.trace()で呼び出し元を追う
trace()
console.trace()
を呼び出した場所に至るまでの呼び出し経路を出力してくれるらしいので、
試してみます。const a = (s) => b(s); const b = (s) => c(s); const c = (s) => console.trace(s); a("trace()");呼び出し元を追えているのがわかりますね!
trace()はフォーマット可能(Chromium)
trace()
のログ、もうちょっと賑やかにしてみたくなりませんか?
Chromeでフォーマットできるか試してみました!const a = (s) => { console.trace(`%c a: %s`, "color: red; font-size: 30px; background: linear-gradient(transparent 60%, #ffff66 0%); ", s); b(s); }; const b = (s) => { console.trace(`%c b: %s`, "color: blue; font-size: 30px; background: linear-gradient(transparent 60%, #ffff66 0%); ", s); c(s); }; const c = (s) => { console.trace(`%c c: %s`, "color: green; font-size: 30px; background: linear-gradient(transparent 60%, #ffff66 0%); ", s); }; a("trace()");
フォーマットできましたね。
ただ、Firefoxだとフォーマットできないです。残念です!CSSは以下を参考:
CSSでおしゃれな装飾!コピペで見出しをデザインしよう! | みゆ何でもブログcounsole.count()でカウント
count()
呼び出された回数を出力してくれるようです。
第一引数に文字列や数値、オブジェクトなどラベルを渡して複数管理でき、
不必要に複数回呼ばれてそうな処理がありそうなときとか確認するのに便利かなと思います。console.count("a"); console.count("b"); console.count("a");count()だとフォーマットはできませんでした
count()
は第一引数しか見ていないようで複数の引数を渡しても意味ないです、
フォーマットはできません。countReset()でカウンターリセット
countReset()
でカウンターをリセットできます。これはブラウザですこし違いがありました。
Chromeの実行してみた結果です。console.count("a"); console.count("a", "b"); console.count("a", "b", "c"); console.log("リセット!"); console.countReset("a"); console.count("a");Firefoxでも実行してみたものです。
countReset()
のときに0を出力してくれてて、Firefoxのほうがちょっと親切です笑console.group()でコンソールのログに階層をつける
複雑な時のログに使えるかもしれないです。
group()、groupCollapsed()
group()
以降の出力を別レベルにインデントして、インライングループを作ってくれるメソッドです。console.group("1st"); console.group("2nd"); console.group("3rd");
group()
で出力される文字はすこし大きめに表示されるみたいですね。
わかりやすいですね。
groupCollapsed
は最初から折りたたまれた状態で出力します。console.groupCollapsed("1st"); console.groupCollapsed("2nd"); console.groupCollapsed("3rd");group()の引数
group()
、groupCollapsed()
は引数にとる文字列はログとして出力するために使うようで、
引数の文字が同じでも変えても特に結果に影響はありませんでした。
引数を複数指定することも出来ます。group()はフォーマット可能
引数がログに出力するために使われ、複数指定可能なところ、
ちょっと気になりませんか?
group()
もフォーマットできるかなと試してみました。for(let n = 1; n < 10; n++){ console.group(`%c Group:%s `, `font-size: ${n*10}px; font-style: italic; letter-spacing: .1em; color: white; text-shadow: -4px 3px 0 #fa4141, -8px 6px 0 black;background:#fa4141; padding: ${n}px;`,n); }groupEnd()
groupEnd()
1回あたり1階層上に戻れます。
group()
と組み合わせて試してみました。for(let n = 1; n < 7; n++){ console.group(`%c Group:%s `, `font-size: ${n*10}px; font-style: italic; letter-spacing: .1em; color: white; text-shadow: -4px 3px 0 #fa4141, -8px 6px 0 black;background:#fa4141; padding: ${n}px;`,(n + 9).toString(36).toUpperCase()); } for(let n = 6; n > 0; n--){ console.groupEnd(); console.log(`%c Group:%s `, `font-size: ${n*10}px; font-style: italic; letter-spacing: .1em; color: white; text-shadow: -4px 3px 0 #fa4141, -8px 6px 0 black;background:#fa4141; padding: ${n}px;`,(n + 9).toString(36).toUpperCase()); }
groupEnd()
で上の階層に戻れてますね。
ちなみに、これはFirefoxでも同じように動きました!CSSは以下を参考:
CSS見出しデザイン参考100選!コピペ可!どこよりも詳しく解説! | JAJAAANconsole.table()でデータを表で出力
table()で配列を表に出力
普通の配列で試すとこんな感じですね。
比較のためにlog()
とtable()
の2つで出力してみます。const arr = [1,2,3,4,5]; console.log("log()", arr); console.table(arr);
log()
だと配列のオブジェクトが表示されるだけですが、
table()
だと表が出力されて並べ替えも出来ます。
表の下には配列のオブジェクトも表示されているので多めに情報をもらえる感じですね。table()でオブジェクトの配列を表に出力
こちらも、
比較のためにlog()
とtable()
の2つで出力してみます!
この Console Standard の「Summary of formatting specifiers」の表をtable()
を使って再度表にしてます。const descriptions = [] descriptions.push({"Specifier":"%s","Purpose":"Element which substitutes is converted to a string","Type Conversion":"%String%(element)"}); descriptions.push({"Specifier":"%d or %i","Purpose":"Element which substitutes is converted to an integer","Type Conversion":"%parseInt%(element, 10)"}); descriptions.push({"Specifier":"%f","Purpose":"Element which substitutes is converted to a float","Type Conversion":"%parseFloat%(element, 10)"}); descriptions.push({"Specifier":"%o","Purpose":"Element is displayed with optimally useful formatting","Type Conversion":"n/a"}); descriptions.push({"Specifier":"%O","Purpose":"Element is displayed with generic JavaScript object formatting","Type Conversion":"n/a"}); descriptions.push({"Specifier":"%c","Purpose":"Applies provided CSS","Type Conversion":"n/a"}); console.log("log()", descriptions); console.table(descriptions);
オブジェクトの配列だと見やすいですね!
使える用途はありそうです。table()でクラスの配列を表に出力
オブジェクトのKeyが違っていても、
クラスがサブクラスでも、別のクラスでも平気なのか?
平気だろうなと想像できるのですが試してみました。class Data { constructor(id, name) { this.id = id; this.name = name; } } class DummyData { constructor(id, name) { this.id = id; this.name = name; } } class SubData extends Data { constructor(id, name, property, boss) { super(id, name); this.property = property; this.boss = boss; } } const data = new Data(1, "Data"); const dummyData = new DummyData(2, "DummyData"); const subData = new SubData(3,"SubData", 5000, data); console.log("log()", [data, dummyData, subData]); console.table([data, dummyData, subData]); console.table([data, dummyData, subData],["id", "name"]);見ればわかるように、
オブジェクトのkeyと階層が一致していれば同じ列に入れてくれるみたいです。
subdataのbossのData
は文字列でないのでクラス名が出力されているようですね。また
table()
の第一引数のオブジェクトのkeyを
第二引数で配列で指定すれば表に出力する列を設定できるようです。table()で文字列を表に出力は出来ません
表になりません。
log()
で出力したようになります。table()はフォーマットできるかな?
log()
のようになるということは文字列をフォーマットできる?
Chromeで試してみました。console.table(`%cDOLCE%c&%cGABBANA`, "font-size: 64px; font-family:Roboto,Helvetica Neue,sans-serif;", "font-size: 32px; font-family:Roboto,Helvetica Neue,sans-serif; padding: 4px;", "font-size: 64px; font-family:Roboto,Helvetica Neue,sans-serif;");CSS行けましたね。
ただ、table()
の出力する文字列にCSSを適用させられても、表にCSSを適用させることはできなかったです笑ちなみに、Firefoxはフォーマットができなくて文字が出力されるだけでした。
Chromiumのブラウザであればフォーマットが可能です。最後に
楽しんで書いたのですが、
読んで楽しんでいもらえたり、役立つ情報がみつかったなら嬉しいです。
全部紹介したわけじゃないので、参考のページも見てもらえたらより情報が手に入ると思います。もうすぐ年末ですね。
紅白歌合戦でどうなるのかなって気になってるアーティストさんがいるのでちょっと楽しみです。明日は@shintaro_ike さんになります。
よろしくお願いします!ウェブクルーでは一緒に働いてくれる方を絶賛募集中です!
興味のある方はぜひお問い合わせください。
開発エンジニア | 株式会社ウェブクルー参考
動作環境
PC : Windows 10 Pro (64 ビット)
Chormeバージョン: 86.0.4240.198(Official Build) (64 ビット)
Edgeバージョン: 87.0.664.47 (公式ビルド) (64 ビット)
Firefox : 83.0 (64 ビット)参考情報
console - Web API | MDN
https://developer.mozilla.org/ja/docs/Web/API/console
Console Standard
https://console.spec.whatwg.org/[JavaScript]使い分けるだけで今すぐデバッグ効率を上げる、consoleオブジェクトの関数
https://qiita.com/kashira2339/items/874f95aaaa59f4a17d3d
console.logまとめ 2016年夏
https://qiita.com/ykyk1218/items/0f5858d077d43a49cfe2
Node.js v10.0.0でconsole.table()追加&console.log()アップデートに感動したので早速試してみる
https://qiita.com/n0bisuke/items/60e52cde73343bbe7703
console.log()系メソッドまとめ
https://qiita.com/mooriii/items/afad70b19f8150f4f1b1
便利なconsole.xxx
https://qiita.com/ryo2132/items/64c616a4cf0ae8770a9f
【2019年4月版】JavaScriptのconsoleがすごいことになってた。
https://qiita.com/koinori/items/83f119cb2d82c0ca2c1e
JavaScriptでの賢いconsole.log( )の使い方 & その他便利なconsole.xxx( )使い方まとめ (dir・table・warn・groupとか)
https://qiita.com/mtoyopet/items/7274761af5424cee342a
【consoleオブジェクト完全版】ログ出力を極める!
https://qiita.com/sa9ra4ma/items/bc6c12044582126c1be9マスクを付けたサンタとトナカイのイラスト(クリスマス) | かわいいフリー素材集 いらすとや
https://www.irasutoya.com/2020/11/blog-post_552.htmlCSS3でテキストにグラデーションをかける方法
https://marie-web.design/blog/text-gradation/
[CSS]background-clipで文字の形に背景を切り取る
https://qiita.com/teinen_qiita/items/9c23ef3befcb1119bce2
background-clip - CSS: カスケーディングスタイルシート | MDN
https://developer.mozilla.org/ja/docs/Web/CSS/background-clip
CSSでおしゃれな装飾!コピペで見出しをデザインしよう! | みゆ何でもブログ
https://miyu-info.com/midashi/
HTMLとCSSで作れる!見出しに使えるおしゃれな文字装飾サンプル | CJコラム
https://citrusjapan.co.jp/column/cj-column/w006_201803.html
CSS見出しデザイン参考100選!コピペ可!どこよりも詳しく解説! | JAJAAAN
https://jajaaan.co.jp/css/css-headline/
font-familyで指定できるフォント名一覧
https://w3g.jp/sample/css/font-familyHaskellをかける少女
https://qiita.com/Yametaro/items/11dcd3ec26027ff30214
Haskellをかけない中年
https://qiita.com/Yametaro/items/e163affa4b2db48f9957Food & Beverage by Dolce & Gabbana | Italian specialties | Dolce & Gabbana | Dolce & Gabbana
https://world.dolcegabbana.com/food-beverage/
- 投稿日:2020-12-01T21:06:21+09:00
JS オブジェクト,プロパティ,メソッドについて (JSアウトプット)
あいさつ
初めての人は初めまして!知っている人はこんにちは!
中学生バックエンドプログラマーのAtieです!
今回はJSの「オブジェクト」「プロパティ」「メソッド」について学んできたのでアウトプットします!
では!オブジェクト
オブジェクトとは「データと機能をまとめたもの」です
プロパティ
プロパティとは「オブジェクト内のデータ」に相当します
メソッド
メソッドとは「オブジェクト内の機能(関数)」に相当します
超ざっくり説明しました
食べ物を例に説明
先ほどの説明だけではわかりずらいので「食べ物」でたとえてみます!
”食べ物”というオブジェクトの中に”果実”というオブジェクトが入っています
プロパティは”果物”メソッドは”甘い”
そしてその”果物”オブジェクトの中にバナナというオブジェクトが入っています
プロパティは”バナナ”メソッドは”86kcal”同じようにりんごなどもあります
ではこれをコードで再現してみます
main.jslet food = { fruits:[ { name: "banana", kcal: function() { console.log('バナナのカロリーは86kcalです!'); } }, { name: "apple", kcal: function() { console.log('リンゴのカロリーは57kcalです!'); } } ] } console.log(food.fruits[0].name); food.fruits[0].kcal(); console.log(food.fruits[1].name); food.fruits[1].kcal();大きく二つに分かれています
上がオブジェクトの定義部分です
オブジェクトを定義するにはmain.jslet オブジェクト名 = { //オブジェクトの内容 };と定義します
中身を見てみましょうmain.jsfruits:[ { name: "banana", kcal: function() { console.log('バナナのカロリーは86kcalです!'); } }, { name: "apple", kcal: function() { console.log('リンゴのカロリーは57kcalです!'); } } ]fruitsという配列の中にnameというプロパティとkcalというメソッドが入っています
メソッドの処理を実行するとコンソールに「バナナのカロリーは86kcalです!」などと表示されるようになっています
配列については今度解説しますオブジェクト内にアクセスする方法ですが二通りあります
.で区切ってアクセスする方法
mian.jsfood.fruits[0].kcal();こんな感じです
基本この方法でアクセスしますが変数などにアクセスする場合などは二つ目の方法を使う必要があります角カッコを使ってアクセスする方法
main.jsfood["fruits"][0]["kcal"]();.で区切る場合に比べて少しわかりずらいですがいちようこの方法でもアクセスできます
window document
最後にwindow documentについてです
JSはブラウザで動く言語。ブラウザ自体もオブジェクトで定義されていてメソッドやプロパティにアクセスできます
その機能を使うことでDOM操作やデータのやり取りができますmain.jswindow.fetch(); //外部とデータをやり取りする window.document.getElementById("food"); //DOM要素のfoodという要素を収得するdocumentは省略可能です
ほかにもたくさんwindowオブジェクトのメソッドがあります例えばJSでよく使われる「console.log」や「alert」なども実はwindowオブジェクトのメソッドだったのです
main.jswindow.console.log("hello"); window.alert("hello");これでも正常に実行できます
windowというオブジェクトを使うことでJSのお仕事であるデータのやり取りとDOM操作ができるようになります!
まとめ
オブジェクトはデータと機能をまとめたもの
プロパティはデータに相当する
メソッドは機能に相当する
ブラウザもオブジェクトで使うことでJSのお仕事の二つができるようになる最後に
今回の記事は少し短かったですがこれもかなり大切な概念です
ではまた!次の記事で!
AtieのTwitter
しまぶーのIT大学さんのTwitter学習、参考動画
【JavaScript入門 #3】オブジェクト・プロパティ・メソッドについて理解しよう【ヤフー出身エンジニアの初心者向けプログラミング講座】
- 投稿日:2020-12-01T20:55:56+09:00
SweetAlertでメッセージ表示
SweetAlertでメッセージ表示
基本情報
本家
https://sweetalert.js.org/guides/#getting-started
概要
JavaScriptの標準装備でアラート、メッセージを出しても良いが、ひと手間加えて、お洒落にメッセージを表示する。
基本的な使い方
メッセージを出すだけ
sweetAlertSample.jsfunction basicSample(){ swal("基本の使い方"); }タイトルとテキストをつける
sweetAlertSample.jsfunction basicSample2(){ swal("ここにタイトル!", "そして、ここにテキスト!"); }アイコンをつける
sweetAlertSample.jsfunction basicSample3(){ swal("アイコンつき!", "ボタンも付いてる!", "success"); }アイコンの使い分け
使用可能なアイコンは4種類。
success
sweetAlertSample.jsfunction iconSample1(){ swal({ title: "success", text: "successのicon", icon: "success", button: "OK", }); }error
sweetAlertSample.jsfunction iconSample2(){ swal({ title: "error", text: "errorのicon", icon: "error", button: "OK", }); }info
sweetAlertSample.jsfunction iconSample3(){ swal({ title: "info", text: "infoのicon", icon: "info", button: "OK", }); }warning
sweetAlertSample.jsfunction iconSample4(){ swal({ title: "warning", text: "warningのicon", icon: "warning", button: "OK", }); }
- 投稿日:2020-12-01T20:39:38+09:00
ReactがわからないというよりJavaScriptがわからなかったのです
はじめに
Reactはユーザインタフェース構築のためのJavaScriptライブラリです。次のような利点があるため、HTML、CSS、 jQuery を学習し、Web制作者目指しているような方もReactにも興味を持つと良いと思いました。JavaScriptをある程度学習していれば、Reactはそんなに難しくないからです。
- コンポーネントベース
- ブロックを組み立てるようにユーザインタフェースを構築できる
- 見通しがよく、苦痛が少ない
- 一度学習すれば応用が効く
- サーバー上でもレンダーできる
- モバイルアプリケーションの中でも動く(React Native)
Reactを二ヶ月くらい学習し、真似してコードが多少書けるようになった時、私はそのコードにどうにも腑に落ちませんでした?。その頃の私はReact以前にJavaScriptがよくわかっていなかったのです。そんな少し前の自分に説明するつもりで書いてみた記事になります。
サンプルコード1
このサンプルは[Toggle]ボタンをクリックするたびに、ボタンの上の絵文字が?と?の間で切り替わるという簡単なものです。
import React, { useState } from "react"; const App = () => { const [liked, toggleLiked] = useState(false); const handleToggle = () => toggleLiked(!liked); return ( <> <p>useState Sample #1</p> <p>{liked ? "?" : "?"}</p> <button onClick={handleToggle}>Toggle</button> </> ); }; export default App;今の自分「useState(ステートフック)を使用しています。シンプルで何の問題もないですね。」
昔の自分「なんだか、わかったようなわからないような。すっきりしないのです?。そもそもフックってどういうこと?意味があまりわからないです?。」
今の自分「フックは何種類かあるのですが、ステートフックとはクラスコンポーネントでは普通に持つことができるステート(コンポーネントがその内部に変数のように持つオブジェクト)をファンクショナルコンポーネント(関数コンポーネント)にも持たせられるようにするための関数です。それでは、次のコードはわかりますか?」
サンプルコード2
こちら見た目や動きは先程のものと同じです。コードの行数は16から36(うち3行はコメントだけの行)と倍増しました。
import React from "react"; export default function App() { const stateArray = React.useState(false); // stateArray[0]の初期値をfalseにする // 戻り値は以下の配列 // stateArray[0]: true(?) または false(?)が入る // stateArray[1]: stateArray[0]を設定する関数 let liked = stateArray[0]; const setLiked = stateArray[1]; function handleToggle() { if (liked === true) { setLiked(false); } else { setLiked(true); } } if (liked === true) { return ( <div> <p>useState Sample #2</p> <p>?</p> <button onClick={handleToggle}>Toggle</button> </div> ); } else { return ( <div> <p>useState Sample #2</p> <p>?</p> <button onClick={handleToggle}>Toggle</button> </div> ); } }昔の自分「コードが長くなりましたが、大分親しみやすいコードです。React.useSateが要素数2の配列を返し、0番目の要素が設定した値の参照用。1番目の要素が、その値を設定する関数を指し示すわけですね。」
今の自分「サンプルコード1(StackBlitz)の以下の部分は、Reactライブラリのエントリポイント(名前空間)のReactをインポートし、Reactの中にあるuseStateを特定の名前付きでインポートをしています。この辺りの詳細はこちらを見てください。」
import React, { useState } from "react";今の自分「サンプルコード1(StackBlitz)の以下の部分は、分割代入(Destructuring assignment)という構文で、配列から値(あるいはオブジェクトからプロパティ)を取り出して、別個の変数に代入できる書き方になります。
const [liked, toggleLiked] = useState(false);昔の自分「アロー関数は知っているのですが、Appという関数の中に handleToggleという関数があるのはどういうことでしょうか?」
今の自分「これはクロージャというやつです。マトリョーシカ人形の親子で例えると、親(関数)から子(関数)の心の内(子の変数など)は見ることができませんが、子からは親の心の内(親の変数など)を見ることができる仕組みです。」昔の自分「なるほど。この辺り、Reactの話しではなくて、全部JavaScriptの話ですね?」
今の自分「そのとおりです。そして、return で返しているHTMLみたいなタグ構文はJSXと呼ばれるJavaScriptを拡張した書き方です。」
昔の自分「それは教材で習いました。これがコンパイルされて、ブラウザで実行可能なコードに変換するのでしたね?」
今の自分「そうです。上のコードはReactのライブラリの関数を1つ呼んでいることと、JSXが使われている以外は表面上はただのJavaScriptとして理解できます。」
Reactを少し勉強してみて感じたこと
1. JSXを用いたUIコンポーネント作成は楽しくてわかりやすい
- 見通しよく作成でき、他人と分担もしやすいです
<Navbar /> <TodoList /> <Footer />2. 親コンポーネントが子コンポーネントに props を渡す方法は1方向で単純でわかりやすい
- 渡した値は変更されない決まり
- 親が子に関数を渡して子から呼んでもらうことなどができる(コンポーネントに関数を渡す)
3. ファンクショナルコンポーネントはクラスコンポーネントよりシンプルで書きやすい
- これから作るだけならファンクショナルコンポーネントだけでよいと思います
- 必要ならファンクショナルコンポーネント以下のフックを組み合わせるなどして、ローカルステート、グローバルステート、ライフサイクルを扱うなど、クラスコンポーネントと同じようなことができる
4. BabelやWebpackとかのややこしい設定は不要
- Create React AppやNext.jsやGatsbyを使えば知る必要がない
- 環境構築で悩む必要なし
- それらについて概要はこちら
5. CSSは好みに合わせて色々な書き方ができる(CSS とスタイルの使用)
- 個人的にはstyled-componentsが良さそうに思っていますが、賛否両論あるみたいですね。
参考資料
- 投稿日:2020-12-01T20:01:35+09:00
【JS】2次元のオブジェクトの要素を変更・追加する方法
2次元のオブジェクトの要素を変更・追加するには、mapを2回使う必要がある。
2次元の要素の変換をした後、1次元の要素を変換する処理となる。
目次
2次元のオブジェクトの要素を変更する実例
要素の追加
[{a:1, b:2, c:[{d:1, e:2}]}
のようなオブジェクトのプロパティcの値にf:9
を新たに追加する場合は以下のようになる。obj = [{a:1, b:2, c:[{d:1, e:2}]}, {a:3, b:4, c:[{d:3, e:4}]} obj = obj.map( elem => { newC = elem.c.map( x => { return {...x, f: 9} }) return {...elem, c: newC} }) console.log(obj) //[{a:1, b:2, c:[{d:1, e:2, f:9}]}, {a:3, b:4, c:[{d:3, e:4, f:9}]・
obj.map( elem => {処理}
まずは、元のオブジェクトにmapを使って、要素をひつづつ取り出す。・
newC
プロパティcにf:9
を追加した要素を代入する変数を準備。・
elem.c.map( x => {処理})
元のオブジェクトの要素のプロパティcの要素を一つずつ取り出す。ここでは x = {d:1, e:2} 。
プロパティcの中に複数のオブジェクトがある場合は、一つづつ取り出す処理となる。・
return {...x, f: 9}
スプレッド構文でプロパティcを展開して、f:9 を追加した後、再度{ }で閉じる。これを戻り値とする。この処理で、 newC = [{d:1, e:2, f:9}] となる。
・
return {...elem, c: newC}
1次元目のオブジェクトをスプレッド構文で展開し、作成したnewCをプロパティCの値に置き換える。この処理で、 {a:1, b:2, c:[{d:1, e:2, f:9}]} となる。
これを要素の数だけ繰り返し完了となる。
要素の変換
要素を変換する場合は、上記の処理で、プロパティcを展開した時に、変換したいプロパティを指定して値を代入すればOK。
obj = [{a:1, b:2, c:[{d:1, e:2}]}, {a:3, b:4, c:[{d:3, e:4}]} obj = obj.map( elem => { newC = elem.c.map( x => { return {...x, d: 100} }) return {...elem, c: newC} }) console.log(obj) //[{a:1, b:2, c:[{d:100, e:2}]}, {a:3, b:4, c:[{d:100, e:2}]・
{...x, d: 100}
この処理で、プロパティcの要素(x)を展開し、その中のプロパティdの値を100に変換している。
要素の追加と変換
追加と変換を行う場合は、上記の処理を組み合わせる。
obj = [{a:1, b:2, c:[{d:1, e:2}]}, {a:3, b:4, c:[{d:3, e:4}]} obj = obj.map( elem => { newC = elem.c.map( x => { return {...x, d: 100, f: 9} }) return {...elem, c: newC} }) console.log(obj) //[{a:1, b:2, c:[{d: 100, e: 4, f: 9}]}, {a:3, b:4, c:[{d: 100, e: 4, f: 9}]・
{...x, d: 100, f: 9}
dを100に変更し、f: 9を新たに追加している。
指定した要素のみ値を変更/追加
上記の例だと該当するプロパティを持つ要素が一括変換となる。
ifで条件分岐を設けることで、指定した要素のみ変換することができる。
▼2番目の要素のみ変更・追加obj = [{a:1, b:2, c:[{d:1, e:2}]}, {a:3, b:4, c:[{d:3, e:4}]} obj = obj.map( (elem, index) => { if(index==1){ newC = elem.c.map( x => { return {...x, d: 100, f: 9} }) return {...elem, c: newC} } else {return elem} }) console.log(obj) //[{a:1, b:2, c:[{d:1, e:2}]}, {a:3, b:4, c:[{d: 100, e: 4, f: 9}]・
obj.map( (elem, index) => { }
mapの第2引数はインデックス番号を格納する変数となる。今回は、if文の条件としてこれを利用。・
if( index == 1 ){ 処理 }
インデックス番号が1の時のみ、newCを作成する処理を実行する。・
else { return elem }
ifの条件がtrue以外の時は、元の要素をそのまま返す。
要素を変更/追加してから特定の要素を排除
オブジェクトに変更を加え、更に不要な要素を削除する場合は、filterで指定条件に該当する要素を除外すればOK。
例えば、以下のような3つの要素が入った2次元のオブジェクトで、2番目の要素のみプロパティを変更・追加し、1番目の要素を削除する場合を考える。
元のオブジェクト[{a:1, b:2, c:[{d:1, e:2}]}, {a:3, b:4, c:[{d:3, e:4}]}, {a:5, b:6, c:[{d:5, e:6}]}]`↓
処理後[{a:3, b:4, c:[{d: 100, e: 4, f: 9}]}, {a:5, b:6, c:[{d:5, e:6}]}]▼コード実例
obj = [{a:1, b:2, c:[{d:1, e:2}]}, {a:3, b:4, c:[{d:3, e:4}]}, {a:5, b:6, c:[{d:5, e:6}]}] obj = obj.map( (elem, index) => { if(index==1){ newC = elem.c.map( x => { return {...x, d: 100, f: 9} }) return {...elem, c: newC} } else {return elem} }) .filter( (_elem, _index) => { return !(_index == 0) } ) console.log(obj) //[{a:3, b:4, c:[{d: 100, e: 4, f: 9}]}, {a:5, b:6, c:[{d:5, e:6}]}]オブジェクトの追加・変更処理はこれまでと同様。
最後にfilter
で、index番号が0となる要素を除外している。
2次元のオブジェクトを変更する場合は、mapを2つ使う。変換したい次元の数だけmapが入れ子になる。
- 投稿日:2020-12-01T20:00:44+09:00
CSS について深い知識はないけどこれだけ知ってれば何とかなるでしょ、的な雑記帳
CSS が苦手です。border とか margin とかちょろちょろっと触るだけならできますけど、flexbox とか新しいやつはほとんど知りません。とはいえフロントエンド畑にいると、CSS フレームワークを使わずに自力で CSS 書いて見た目を調整する事とか、ありますよね(管理画面系とか)。
CSS について深い知識はないけどこれだけ知ってれば何とかなるでしょ、的な雑記帳としてメモを残しておこうと思います。
ブロック要素をドラッグ&ドロップでリサイズ可能に
TL;DR
-resize
でリサイズ可能になる<header> <div>contents</div> <div>contents</div> <div class="resizable-h">resizable-h</div> </header> <nav class="resizable-v">resizable-v</nav>.resizable-h { min-width: 10em; overflow: hidden; /* これがないとリサイズ効かない */ resize: horizontal; /* 横方向のリサイズ */ background: #516f8d; } .resizable-v { min-height: 10em; overflow: hidden; /* これがないとリサイズ効かない */ resize: vertical; /* 縦方向のリサイズ */ background: #516f8d; }dl>ddの空要素にハイフンをデフォルト表示
TL;DR
-dd:empty
で空要素の判定ができる<dl> <dt>Artist</dt> <dd>Queen</dd> <dt>Title</dt> <dd>Made in Heaven</dd> <dt>Release date</dt> <dd></dd> <!-- 空要素 --> <dt>Review</dt> <dd>this is the best Album</dd> </dl>dd:empty::before { content: '-'; }フォーカスした時にバリデーションエラー表示
TL;DR
-focus-within
を使えば div タグに「子要素がフォーカスを持っている時」の CSS を書ける
-focus-visible
でフォーカスリングを消す
-caret-color
でキャレットの色を変更<!-- FontAwesome 使用 --> <div> <i class="fa fa-user"></i> <input type="text"> </div>input { caret-color: red; } /* 子要素がフォーカスを持つ時に適用される CSS */ div:focus-within i { color: #f56161; } div:focus-within input { border: 1px solid #f56161; } /* 影つけて強調 */ input:focus { box-shadow: 2px 2px 2px 0 #f56161; } /* フォーカスリングを消す */ input:focus-visible { outline: none; }横並びグリッドレイアウト
TL;DR
-grid-template-columns: repeat(3, 30%)
で 30 %幅の列を 3 個作る
-grid-template-columns: 100px 90px 80px
とすれば列ごと個別の幅指定も可
-grid-template-rows
とすれば列でなく行で同じ事ができる<div> <article><img src="https://picsum.photos/id/237/200/300"/></article> <article><img src="https://picsum.photos/id/1025/200/300"/></article> <article><img src="https://picsum.photos/id/1062/200/300"/></article> </div>div { display: grid; grid-template-columns: repeat(3, 30%); grid-gap: 3%; }ライブラリ使わないフワッと出るダイアログ
TL;DR
-<dialog>
設置
-document.getElementById("id").showModal()
でダイアログオープン
- CSS でtransition
を使えばフワッと感を出せる<dialog id="dialog"> <!-- form がないとボタンが反応しないので必須 --> <form method="dialog"> <p>これ便利じゃないですか?</p> <!-- button の value が dialog.returnValue になる --> <button value="yes">yes</button> <button value="no">no</button> </form> </dialog> <button id="trigger">Open dialog</button>const $trigger = document.getElementById("trigger"); const $dialog = document.getElementById("dialog"); $trigger.addEventListener("click", () => { $dialog.showModal(); }); $dialog.addEventListener("close", () => { $result.value = $dialog.returnValue; });dialog { /* opacity 効かせるために display 必須 */ display: block; opacity: 0; } dialog[open] { /* 2秒間かけて opacity を 0 > 1 に変化させる(フワッ) */ opacity: 1; transition-property: opacity; transition-duration: 2s; transition-timing-function: ease; }
- 投稿日:2020-12-01T19:27:22+09:00
膝の写真を送ると変形性膝関節症か判定するbotを作ったので、膝が痛いご両親・ご家族に使ってください。
「膝のレントゲンなんて持ってないっすよ。」
以前に書いた記事、『誰が使うかわからないけど、膝のレントゲン写真を送ったら、その膝がどの程度痛んでいるのか教えてくれるラインbotを作ってみた。』を様々な人に紹介したところ、「すごくいいと思うけど、膝のレントゲンなんて持ってないよ(笑)」と多くの方から生暖かい目で諭されました。
安心してください。想定の範囲に入ってますよ。
そこで、今回は膝の外表面写真からその膝が変形しているかどうか判定するbotを作りました。
挙動は以下の通りです。精度は微妙だけど、外表面から変形の程度を判断するbotも完成。#protoout pic.twitter.com/S0kDG0FGS6
— 北城雅照@足立慶友整形外科 (@kutuyanomusuko) November 29, 2020* 「変形性膝関節症って何?」という方は、僕が書いたこちらの記事をぜひお読みください。
変形性膝関節症とは:その治療法・進行予防について注意1) 「変形性膝関節症って何?」という方は、僕が書いたこちらの記事をぜひお読みください。
変形性膝関節症とは:その治療法・進行予防について注意2) このボットはあくまで啓蒙活動の一貫で作成したもので、正確な診断ツールではありません!最終的な診断については、おかかりいただいた先生にお伺いください。
システムの概要
開発環境について
今回はAIメーカーを利用して実装いたしました。
実装の内容については上記の記事『誰が使うかわからないけど、膝のレントゲン写真を送ったら、その膝がどの程度痛んでいるのか教えてくれるラインbotを作ってみた。』をご確認ください。判定の方法の医学的根拠について
今回、判定の方法に用いた医学的根拠について詳述します。興味のある方はお読みください。
「早く使いたい!」という方は、一番下のQBコードまで進んでください。1)膝の痛みを主訴に受診された患者様の膝外表面写真を、膝を中心に足関節が入るように撮影
2)上記患者様の膝のレントゲン写真を、経験のある整形外科(つまり私)が、KL分類をもとに分類
*ちなみにKL分類は下記の分類です。
3)レントゲンの分類をもとに、外表面写真を正常群22例(グレード0とグレード1)、変形性膝関節症群35例(グレード2,グレード3,グレード4)の2群に分類
4)AIメーカーに正常膝と変形性関節症膝の2つのラベルを用意し、そこに画像をアップロードして学習
完成したラインbot
使用上注意点と今後の課題
1)まだまだ精度が低いので写真を増やしたり、撮り方を統一したりなどの調整を行なっていきます。
2)膝を中心に足首が入るように素足で写真を撮ってください。
3)繰り返しになりますが、診断ツールではありません。啓蒙ツールです。ご利用は計画的にお願いします。その他の記事
1) 近すぎると小池都知事が『密です。』と連呼するデバイスを作ったら腹筋が崩壊したので、皆さんにも試して欲しい。
2) 憧れのギニュー特戦隊の誰に似てるか判定するLINEbotを作ったから、ぜってぇ見てくれよなっ!
- 投稿日:2020-12-01T19:25:04+09:00
Most Breakthrough Generatorではありますまいか
この記事は
Works Human Intelligence社、ワークス社とは関係ない、文責:個人の趣味の投稿です。
私は何がやりたいのか、あるいは何がやりたくないのかという話、私の場合 を考えたついでにバランスを取りたくて書きました。Most Breakthrough Generatorとは
日常の何気ない文章をものすごい問題解決にしてくれるブレイクスルーエンジンです。クソアプリアドベントカレンダー がとても賑わっているようですが、これは決してクソアプリではなくモストブレイクスルーアプリです。
https://e99h2121.github.io/breakthrough-generator/
というかジョークおもちゃです、こんなの業務中に作っててごめんなさい
なるほど、ブレイクスルーしています。Most Breakthrough
Most Breakthroughとは、かつてのワークスアプリケーションズ社時代の社内表彰です。英語としてどうなの。
裏側
https://github.com/e99h2121/breakthrough-generator/blob/main/index.html
完全にクライアントサイド
javascriptだけで作っています。javascriptはお手軽で良かった。
全てクライアントサイドで解析を行うため、セキュリティの観点から見て安全です。Ver.1 ランダム、文字列置換
ver1.js<script type="text/javascript"> function breakthrough() { const messages = ['問題解決こそが仕事でありますぞ。', '本当の仕事とは、何か大きな問題に直面したときから始まる。', 'プロ、もしくはプロであるべきポジションの仕事人の無自覚や無神経さを「まぬけ」と呼びます。', '例えば「どこでもつながる携帯電話」というのを考えるとしましょう。これは理想として言えば、世界中のどこに行ってもつながる、地下だろうが高い山のてっぺんだろうが、太平洋のど真ん中だろうが通話ができるっていうのが理想じゃありませんか?']; const messageNo = Math.floor( Math.random() * messages.length); const suffix = ['ではありますまいか。', 'ぞ。']; const suffixNo = Math.floor( Math.random() * suffix.length); var mackytext; mackytext = messages[messageNo] + '\n'; mackytext = mackytext + '\n' + document.getElementById('origin').value.replace(/。/g, suffix[suffixNo]); document.getElementById('mackytext').value = mackytext; } </script>index.html<TD><textarea cols="80" rows="5" id="origin"></textarea></TD> (中略) <p><input type="button" value="ブレイクスルーする" onclick="breakthrough();"></p> (中略) <p><textarea cols="80" rows="10" id="mackytext">ここに表示されるのですぞ。</textarea></p>30分くらいでつくりましたが、細かい命名には、気をつけました(色々な意味において)。
Ver.1 参考文献
特定の文字列を全て置換する[Javascript]
文字列ランダム表示Ver.2 形態素解析ライブラリとTweet
ver2.js<script type="text/javascript" src="tiny_segmenter.js" charset="UTF-8"></script> <script type="text/javascript" charset="UTF-8"> var segmenter = new TinySegmenter(); function breakthrough() { var segmenter = new TinySegmenter(); var segs = segmenter.segment(document.getElementById('origin').value); var conv = ""; for (const elem of segs) { var str = elem; str = str.replace("です", "ですぞ") str = str.replace("でした", "でしたぞ") str = str.replace("ます", "ますまいか"); str = str.replace("た", "たぞ"); conv = conv + str; } const messages = ['問題解決こそが仕事でありますぞ。', '本当の仕事とは、何か大きな問題に直面したときから始まる。', 'プロ、もしくはプロであるべきポジションの仕事人の無自覚や無神経さを「まぬけ」と呼びます。', '例えば「どこでもつながる携帯電話」というのを考えるとしましょう。これは理想として言えば、世界中のどこに行ってもつながる、地下だろうが高い山のてっぺんだろうが、太平洋のど真ん中だろうが通話ができるっていうのが理想じゃありませんか?']; const messageNo = Math.floor( Math.random() * messages.length); const suffixNo = Math.floor( Math.random() * suffix.length); var mackytext = ""; //mackytext = mackytext + '\n' + document.getElementById('origin').value.replace(/。/g, suffix[suffixNo]); mackytext = conv + '\n'; mackytext = mackytext + messages[messageNo] ; document.getElementById('mackytext').value = mackytext; } function tweet() { var left = Math.round(window.screen.width / 2 - 275); var top = (window.screen.height > 420) ? Math.round(window.screen.height / 2 - 210) : 0; window.open( "https://twitter.com/intent/tweet?text=" + encodeURIComponent(document.getElementById("mackytext").value + " at: https://e99h2121.github.io/breakthrough-generator/" ), null, "scrollbars=yes,resizable=yes,toolbar=no,location=yes,width=550,height=420,left=" + left + ",top=" + top); } </script>もう少し真面目にやりだしました。形態素解析Javascriptという、便利なライブラリがありました。
Ver.2 参考文献
TinySegmenter:Javascriptだけで書かれたコンパクトな分かち書きソフトウェア
オープンソースのコードを取り込んだ時のライセンス表記について
オリジナルのツイートボタンの呟き内容をjavascriptsで動的に変える方法公開
当然自分でホストすれば良いんですが、これを発見
https://sites.google.com/view/how-to-with-new-sites/embeds/embed-with-htmljavascript
することで、まずは社内でも簡単に公開できました。Editey というものもあるようだ。参考:WebブラウザでUnityで作ったシーンを歩き廻ろう!
考えているうちに、https://pages.github.com/ で良いか。となりました。解説
私の会社の、前の名前の会社の創業者、「牧野正幸」氏の謎の口癖?ブログ上の人格?に多分にエナジャイズされた
社内ウケを狙ったジョークおもちゃモストブレイクスルーです。元ネタ
在職エントリ
私の勤めるWorks Human Intelligence社は2019年、ワークスアプリケーションズ社から分割して誕生しました。引用:https://www.works-hi.co.jp/news/20190801
当時のことを黒歴史のように語るのはタブーに触れる行為のように多々おもわれる節を感じたり、感じなかったり、かもしれませんが、内部で新卒時代からそれなりに業務をこなせるようになった今を過ごしてきたひとりにとって、そしてひとりの「開発」にとって、会社の「枠」なんて、わりとどうでもいいことです。単にそこに「1000をゆうに数える日本の大企業であるお客様の業務を支えている」という圧倒的な面白みがあるから相変わらず続けている、というのが、私個人の気持ちです。
「卒業生」もそれなりに多くの分野で活躍しており、私も「卒業してこそ一人前」「まだまだ自分はその度胸がない」な~という気持ちでいるのですが、弊社、開発者が実名で、会社のタイトルを明かしつつも内部の雰囲気を語るという機会がなぜかない(少ない?)。中はそれなりに相変わらず楽しいよと、どこかで一発、在職エントリをかましてやりたいなと思っていました。偉い人から怒られそうな気もしますが、私なりの会社愛として受け止めてくれると信用する意味で投稿しています。
後日消されていたら察してください。
以下記事が最近トレンドに載ったのでこちらも宣伝。
Delphiなんかで開発していて恥ずかしくないの?
「失敗を許容する」なんて言われても失敗したくないです
真面目なことも書いてるよまた12/5 には人事プロダクト「COMPANY」のバッチ処理の裏側を、きちんと投稿する予定です。
Develop fun!を体現する Works Human Intelligence Advent Calendar 2020
Develop fun!を体現する Works Human Intelligence #2 Advent Calendar 2020では!
- 投稿日:2020-12-01T19:25:04+09:00
Most Breakthrough Generator
この記事は
Works Human Intelligence社、ワークス社とは関係ない、文責:個人の趣味の投稿です。
私は何がやりたいのか、あるいは何がやりたくないのかという話、私の場合 を考えたついでにバランスを取りたくて書きました。Most Breakthrough Generatorとは
日常の何気ない文章をものすごい問題解決にしてくれるブレイクスルーエンジンです。クソアプリアドベントカレンダー がとても賑わっているようですが、これは決してクソアプリではなくモストブレイクスルーアプリです。
https://e99h2121.github.io/breakthrough-generator/
というかジョークおもちゃです、こんなの業務中に作っててごめんなさい
なるほど、ブレイクスルーしています。Most Breakthrough
Most Breakthroughとは、かつてのワークスアプリケーションズ社時代の社内表彰です。英語としてどうなの。
裏側
https://github.com/e99h2121/breakthrough-generator/blob/main/index.html
完全にクライアントサイド
javascriptだけで作っています。javascriptはお手軽で良かった。
全てクライアントサイドで解析を行うため、セキュリティの観点から見て安全です。Ver.1 ランダム、文字列置換
ver1.js<script type="text/javascript"> function breakthrough() { const messages = ['問題解決こそが仕事でありますぞ。', '本当の仕事とは、何か大きな問題に直面したときから始まる。', 'プロ、もしくはプロであるべきポジションの仕事人の無自覚や無神経さを「まぬけ」と呼びます。', '例えば「どこでもつながる携帯電話」というのを考えるとしましょう。これは理想として言えば、世界中のどこに行ってもつながる、地下だろうが高い山のてっぺんだろうが、太平洋のど真ん中だろうが通話ができるっていうのが理想じゃありませんか?']; const messageNo = Math.floor( Math.random() * messages.length); const suffix = ['ではありますまいか。', 'ぞ。']; const suffixNo = Math.floor( Math.random() * suffix.length); var mackytext; mackytext = messages[messageNo] + '\n'; mackytext = mackytext + '\n' + document.getElementById('origin').value.replace(/。/g, suffix[suffixNo]); document.getElementById('mackytext').value = mackytext; } </script>index.html<TD><textarea cols="80" rows="5" id="origin"></textarea></TD> (中略) <p><input type="button" value="ブレイクスルーする" onclick="breakthrough();"></p> (中略) <p><textarea cols="80" rows="10" id="mackytext">ここに表示されるのですぞ。</textarea></p>30分くらいでつくりましたが、細かい命名には、気をつけました(色々な意味において)。
Ver.1 参考文献
特定の文字列を全て置換する[Javascript]
文字列ランダム表示Ver.2 形態素解析ライブラリとTweet
ver2.js<script type="text/javascript" src="tiny_segmenter.js" charset="UTF-8"></script> <script type="text/javascript" charset="UTF-8"> var segmenter = new TinySegmenter(); function breakthrough() { var segmenter = new TinySegmenter(); var segs = segmenter.segment(document.getElementById('origin').value); var conv = ""; for (const elem of segs) { var str = elem; str = str.replace("です", "ですぞ") str = str.replace("でした", "でしたぞ") str = str.replace("ます", "ますまいか"); str = str.replace("た", "たぞ"); conv = conv + str; } const messages = ['問題解決こそが仕事でありますぞ。', '本当の仕事とは、何か大きな問題に直面したときから始まる。', 'プロ、もしくはプロであるべきポジションの仕事人の無自覚や無神経さを「まぬけ」と呼びます。', '例えば「どこでもつながる携帯電話」というのを考えるとしましょう。これは理想として言えば、世界中のどこに行ってもつながる、地下だろうが高い山のてっぺんだろうが、太平洋のど真ん中だろうが通話ができるっていうのが理想じゃありませんか?']; const messageNo = Math.floor( Math.random() * messages.length); const suffixNo = Math.floor( Math.random() * suffix.length); var mackytext = ""; //mackytext = mackytext + '\n' + document.getElementById('origin').value.replace(/。/g, suffix[suffixNo]); mackytext = conv + '\n'; mackytext = mackytext + messages[messageNo] ; document.getElementById('mackytext').value = mackytext; } function tweet() { var left = Math.round(window.screen.width / 2 - 275); var top = (window.screen.height > 420) ? Math.round(window.screen.height / 2 - 210) : 0; window.open( "https://twitter.com/intent/tweet?text=" + encodeURIComponent(document.getElementById("mackytext").value + " at: https://e99h2121.github.io/breakthrough-generator/" ), null, "scrollbars=yes,resizable=yes,toolbar=no,location=yes,width=550,height=420,left=" + left + ",top=" + top); } </script>もう少し真面目にやりだしました。形態素解析Javascriptという、便利なライブラリがありました。
Ver.2 参考文献
TinySegmenter:Javascriptだけで書かれたコンパクトな分かち書きソフトウェア
オープンソースのコードを取り込んだ時のライセンス表記について
オリジナルのツイートボタンの呟き内容をjavascriptsで動的に変える方法公開
当然自分でホストすれば良いんですが、これを発見
https://sites.google.com/view/how-to-with-new-sites/embeds/embed-with-htmljavascript
することで、まずは社内でも簡単に公開できました。Editey というものもあるようだ。参考:WebブラウザでUnityで作ったシーンを歩き廻ろう!
考えているうちに、https://pages.github.com/ で良いか。となりました。解説
私の会社の、前の名前の会社の創業者、「牧野正幸」氏の謎の口癖?ブログ上の人格?に多分にエナジャイズされた
社内ウケを狙ったジョークおもちゃモストブレイクスルーです。元ネタ
在職エントリ
私の勤めるWorks Human Intelligence社は2019年、ワークスアプリケーションズ社から分割して誕生しました。引用:https://www.works-hi.co.jp/news/20190801
当時のことを黒歴史のように語るのはタブーに触れる行為のように多々おもわれる節を感じたり、感じなかったり、かもしれませんが、内部で新卒時代からそれなりに業務をこなせるようになった今を過ごしてきたひとりにとって、そしてひとりの「開発」にとって、会社の「枠」なんて、わりとどうでもいいことです。単にそこに「1000をゆうに数える日本の大企業であるお客様の業務を支えている」という圧倒的な面白みがあるから相変わらず続けている、というのが、私個人の気持ちです。
「卒業生」もそれなりに多くの分野で活躍しており、私も「卒業してこそ一人前」「まだまだ自分はその度胸がない」な~という気持ちでいるのですが、弊社、開発者が実名で、会社のタイトルを明かしつつも内部の雰囲気を語るという機会がなぜかない(少ない?)。中はそれなりに相変わらず楽しいよと、どこかで一発、在職エントリをかましてやりたいなと思っていました。偉い人から怒られそうな気もしますが、私なりの会社愛として受け止めてくれると信用する意味で投稿しています。
後日消されていたら察してください。
以下記事が最近トレンドに載ったのでこちらも宣伝。
Delphiなんかで開発していて恥ずかしくないの?
「失敗を許容する」なんて言われても失敗したくないです
真面目なことも書いてるよまた12/5 には人事プロダクト「COMPANY」のバッチ処理の裏側を、きちんと投稿する予定です。
Develop fun!を体現する Works Human Intelligence Advent Calendar 2020
Develop fun!を体現する Works Human Intelligence #2 Advent Calendar 2020では!
- 投稿日:2020-12-01T18:52:22+09:00
strapi+nuxt.jsでアドベントカレンダーを作ろう
strapiを触ってみよう
巷でウワサのheadlessCMS「strapi」を触ってみました。
アドベントカレンダーの記事ということで、簡単なアドベントカレンダーを作ってみます。
記事表示の部分はnuxt.jsを利用します。strapiの構築
npxコマンド一発で簡単に初期構築を行ってくれます。
npx create-strapi-app advent_api --quickstartユーザ登録を済ませたら、記事用のコンテンツタイプ(データ定義)を作成します。
項目の名前とデータ型、その他設定(一意制約や必須など)を選択することで簡単にデータ項目を増やしていくことができます。コンテンツタイプを作成すると、下記の基本的なAPIが自動で生成されます。
- 一覧
- 件数
- 詳細
- 追加
- 更新
- 削除
この段階ではAPIのアクセスが許可されていないので、デフォルトで存在するpublicロールに、取得系APIのアクセス権限を設定してあげます。
試しに記事を2件ほど書いてみます(投稿もAPI経由で行うことができますが、時間の都合上デフォルトの管理画面上で作成します)
この状態で、自動生成された記事一覧のAPIにアクセスしてみると...
http://localhost:1337/articles/記事情報のjsonが取得できました。
[ { "id": 1, "title": "シュトレンを作ってみた", "text": "## シュトレンとは\n\nシュトレンというお菓子を知っていますか?\nシュトレンとは・・・", "date": "2020-12-01", "published_at": "2020-11-30T12:16:18.523Z", "created_at": "2020-11-30T12:16:15.854Z", "updated_at": "2020-12-01T03:07:40.608Z" }, { "id": 2, "title": "アドベントカレンダーを作ろう", "text": "## はじめに\n\n**アドベントカレンダー**作りたい... 作りたくない?\n", "date": "2020-12-02", "published_at": "2020-11-30T12:17:53.678Z", "created_at": "2020-11-30T12:17:51.696Z", "updated_at": "2020-12-01T03:06:19.338Z" } ]Viewの作成(Nuxt.js)
APIから取得したデータを表示する部分はnuxt.jsで作ります。
こちらもコマンド一発で初期構築。
対話式で利用するプラグインなどを設定できるので、
CSSフレームワークとしてVuetify を導入します。npx create-nuxt-app advent_front/pages/配下に、記事一覧ページと記事詳細ページを作ります。
記事一覧
Vuetifyにはカレンダー用のmoduleが用意されているのでこれを利用します。
apiから取得したデータを詰め替えて、カレンダーのイベントとして登録していきます。index.vue<template> <v-container> <v-sheet height="80vh"> <v-calendar light height="80vh" start="2020-12-01" end="2020-12-25" :events="events" @click:event="showEvent"></v-calendar> </v-sheet> </v-container> </template> <script> import axios from 'axios' export default { data() { return { events:[] }}, methods: { showEvent ({ nativeEvent, event }) { this.$router.push(`/${event.id}`) } }, async mounted() { const {data} = await axios.get("/api/articles/"); this.events = data.map(el=>{ return { id: el.id, start: new Date(el.date), name: el.title, timed: false, } }) } } </script>記事詳細
nuxtはファイル名から自動でルーティングを生成してくれますが、ファイル名の先頭に_をつけた場合パスパラメータとして認識され、
$route.params
で取得することが可能です。
また、本文はmarkedライブラリを利用してmarkdownからhtmlに変換しています。_id.vue<template> <v-container> <v-sheet height="80vh" class="pa-3"> <div tag="h2" class="headline" v-html="article.title"></div> <div v-html="article.date" class="mb-3"></div> <v-sheet v-html="article.text"> </v-sheet> <v-btn class="mt-5" to="/">カレンダーに戻る</v-btn> </v-sheet> </v-container> </template> <script> import axios from 'axios' import marked from 'marked' export default { data() { return { article: { title: "hoge", date: "2020-12-01", text: "hogehoge", } }}, props:["id"], methods: { md2Html(val) { return marked(val); } }, async mounted() { const {data} = await axios.get("/api/articles/"+this.$route.params.id); this.article = { title: data.title, date : data.date, text : this.md2Html(data.text) } } } </script>今回はstrapi,nuxtそれぞれlocalhostの別portで動かす(別ドメイン扱いになる)ため、nuxtでproxy設定を行うことでクロスオリジン制約を回避します。
nuxt.config.jsmodules: [ '@nuxtjs/axios', '@nuxtjs/proxy' ], proxy: { '/api': { target: 'http://localhost:1337', pathRewrite: { '^/api' : '/' } } },これでこのように表示されます。見た目ダサいのは気にしない
http://localhost:3000/http://localhost:3000/1/完走した感想
実際に利用するには権限周りの設定などもう少し作り込む必要がありますが、それでもstrapi+nuxtの組み合わせはかなり簡単に初期構築を行えるなと感じました。
今回のような超小規模だとWordpressでよくね?となってしまう感はあるものの、
- Nuxtできちんとサーバサイドレンダリングをする
- 並行してアプリ化もする
など、headlessCMSの強みを活かせるような要件では強力だと思います。
FORK Advent Calendar 2020
1日目 Google Cloud Dataproc でデータ解析基盤を構築 @numatch00
3日目 Coming soon @sugar-engine
- 投稿日:2020-12-01T18:48:18+09:00
JavascriptのforEachでindexの使用例
forEachのindexとかarrayの使いどころがわからなかった
forEachでコールバック関数を利用するのみでindex,arrayを使ったことがなかった。
face-api.jsの中身を見ていてああ、なるほどと思ったのでまとめます。例
face-api.jsの中で使われていたコードの雰囲気
test.jsarray.slice(1).forEach(({ x, y }, index) => { const from = array[index]; ctx.moveTo(from.x, from.y); ctx.lineTo(x, y); }) ctx.stroke(); //~~~これは顔の特徴点と特徴点同士を線でつなぎ、canvas上に描画する処理の一部。↓の青線を引く。
slice関数で1番目以外の点を取得し、それぞれについて一つ前の点から次の点へ直線を引くという流れです。
あとがき
いままで使える機会があったが、わざわざ別で変数を用意して繰り返しインクリメントさせる
という非常にスマートでない方法でやっていた。。。
apiの中身を見てみるといろいろと発見がある!
- 投稿日:2020-12-01T18:47:22+09:00
【JS】オブジェクトから要素を削除する方法。filterとfindの活用
オブジェクトから該当する要素を削除する方法について。
例えば、以下のようなオブジェクトがあった場合に、指定した要素を削除する場合を考える。
obj = [{a:1, b:2}, {a:1, b:3}, {a:2, b:3}, {a:3, b:4}]目次
元のオブジェクトを利用する場合
元のオブジェクトを使って除外する要素を指定する場合は判定式が使える。
要素が1つの場合
要素が一つの場合は、filterを使えば実現可能。例えば、配列番号3の
{a:3, b:4}
を削除する場合は以下のような式になる。obj = [{a:1, b:2}, {a:1, b:3}, {a:2, b:3}, {a:3, b:4}] removeObj = obj[3] res = obj.filter( elem => elem != removeObj) console.log(res) //[{a:1, b:2}, {a:1, b:3}, {a:2, b:3}]
obj.filter( elem => elem != removeObj)
- objの要素を一つづつ抜き出し、elemという変数に代入する
=>
以降の処理がtrueの場合のみ要素を抜き出す。- つまり、要素がremoveObjと一致する場合は抜き出さない(削除)する処理となる。
removeObj = obj[3]
のように、元のオブジェクトを使って要素を指定しているため、要素どうしの比較が可能。
要素が2つ以上の場合
要素が2つ以上の場合はfilterとfindを使う必要がある。配列番号0と1の要素を排除する場合は以下のようになる。
obj = [{a:1, b:2}, {a:1, b:3}, {a:2, b:3}, {a:3, b:4}] removeObj2 = [obj[0], obj[1]] res2 = obj.filter( elem => !removeObj2.find( x => x == elem )) console.log(res2) //[{a:2, b:3}, {a:3, b:4}]
obj.filter( elem => 処理)
- filterを使ってobjの要素を一つ一つ抜き出す。(変数elemに代入)
!removeObj2.find( x => x == elem )
- findは処理結果がtrueになった要素を返す処理。
!
をつけ、排除したい要素とobjの要素が一致した場合はfalseを返す。- filterはtrueの場合は要素を抽出するが、falseの場合は抽出しない(除外する)
元のオブジェクトを利用しない場合
元のオブジェクトを利用しない場合は、filterとfindを使うことで、指定した要素を排除できる。
obj = [{a:1, b:2}, {a:1, b:3}, {a:2, b:3}, {a:3, b:4}] removeObj = [{a:1, b:2}, {a:3, b:4}] res = obj.filter( elem => !removeObj .find( x => x.a == elem.a && x.b == elem.b ) ) console.log(res) //[{a:1, b:3}, {a:2, b:3}]元のオブジェクトからfilterを使って要素を一つづつ取り出し、中のプロパティを一つ一つ比較していく必要がある。
removeObj.find( 変数 => 処理 )
- 処理結果がtrueになった最初の要素を返す。
x => x.a == elem.a && x.b == elem.b
removeObjのプロパティaとbが元のオブジェクトの要素と一致した場合にその値を返す。(xはremoveObjの要素を一つ一つ取り出した変数)!
をつけ、排除したい要素とobjの要素が一致した場合はfalseを返す。
どれか一つのプロパティが一致する要素を削除する場合
複数あるプロパティの中で、a:1となっている要素を全て除外する場合はfilterのみで実現可能。
obj = [{a:1, b:2}, {a:1, b:3}, {a:2, b:3}, {a:3, b:4}] res = obj.filter( elem => !(elem.a == 1) ) console.log(res) //[{a:2, b:3}, {a:3, b:4}]
!(elem.a == 1)
filterで抜き出した要素(elem)のプロパティaが1となる場合はfalseを返し除外する。
(補足)filterのみで記述する場合
要素が1つの場合は、findを使わずfilterのみで記述することも可能だが汎用性がないため特に不要。
記述は以下のようになる。
obj = [{a:1, b:2}, {a:1, b:3}, {a:2, b:3}, {a:3, b:4}] removeObj3 = [{a:3, b:4}] res3 = obj.filter( elem => !(removeObj3[0].a == elem.a && removeObj3[0].b == elem.b ) ) console.log(res3) //[{a:1, b:2}, {a:1, b:3}, {a:2, b:3}]
removeObj3[0].a
のようにプロパティの値を直接指定し、比較している。
- 投稿日:2020-12-01T18:25:18+09:00
YOUは何処から日本へ?(ページにアクセスしたユーザーがどこから来たか調べる)
アクセスしたユーザーの国や地域、IPアドレス取得
案件で多言語サイトのデフォルト言語をユーザーごとに出し分けたいという要望があったため
フロント、サーバー側で国や地域、IPアドレスを取得する方法を調査したJavaScripts で対応
JS単体では端末のIPを取得することはできないがAPIを利用することで取得できる
以下は中でもフリーのサービスを抜粋したもの1. ipinfo.io
アクセスするとIPを取得して表示してくれるサービス。
JSON形式で情報が返却される。
アカウント作成とアクセス回数に上限がある
ipinfo.io2. Geolocation API
WiFi、GPS、IPアドレスなどから位置情報を取得するAPI。
JSON形式で情報が返却される。
位置情報の取得にはユーザーの許可が必要なため拒否されれば当然取得できない。
WiFiや緯度経度からの取得はGoogleが提供していることが多いため中国などでは利用できないことがある。
Geolocation API3. ipify
フリーのIP取得サービス。
JSON形式で情報が返却出来る。
上限は無し。
ipify
API4. jsonip.com
無料無制限のIP取得サービス。
JSON形式でIPを取得できるがおまけに広告とドキュメントのURLが含まれている。
以前は政治的メッセージも含まれていたがなくなったらしい。
上限なし。
jsonip.com
API5. JSON Test
フリーのIP等取得サービス。
IPアドレス単体やリクエストヘッダー情報をJSON形式で取得できる。
名前のせいで情報が見つけにくい。
JSON Test
API6. ipapi.co
フリーと商用ライセンスのあるIP等取得サービス。
アカウント登録が必要。
IPだけでなく国や地域、緯度経度など詳細なデータをJSON形式で取得できる。
珍しくドキュメントもそろっているので親切。
ipapi.coPHP で対応
PHPはIPアドレスを取得できるが国や地域を取得するにはライブラリかAPIが必要になる(一部有料)
1. IPinfoAPI
IPアドレスを渡すと国や地域のコードをJSON形式で取得できるサービス
上限が毎秒2回まで。2. Geo IP2
MaxMind社から提供されたデータベースファイルをサーバー側に設置しアクセスしたIPで検索してどの国や地域からアクセスがあったかを調べる。
無償版ライセンスでは国まで、有償版では地域まで取得できるので用途によって使い分け可能。
Geo IPAWS で対応
AWSのCloudFront-Viewer-Country はIPアドレスから判断したアクセス元の国コードを返却するためAWSを利用していれば対応可能。
AWSまとめ
JavaScriptsは単体ではIPを取得できないがAPIを使用すればIPもしくは国コードなどのアクセス元情報が取得できる。
ただしIPアドレスの取得方法によっては対象外の国や地域があるため注意が必要。PHPはIPアドレスは単体で取得可能だがアクセス元の国や地域のコードを取得するにはライブラリ、APIが必要。
ライブラリのライセンスは詳細な情報が必要であれば有償版、国コードが必要なだけであれば無償版でよい。その案件がAWSを利用しているのであればCloudFront-Viewer-Country で対応可能
- 投稿日:2020-12-01T18:23:58+09:00
JavaScriptでコラッツ予想の動作を実行
コラッツ予想の動作を確認してみる
JavaScriptでコラッツ予想の動作を実行するプログラムを作成してみました。
・自然数を入力する
・「偶数なら2で割り、奇数なら3倍して1を足す」という操作を繰り返す
・1になったら終了する上記操作で必ず1になってプログラムが停止する、というのが「コラッツ予想」です。
「予想」と呼ばれるとおり、2020年現在証明はされていません。CollatzProblem.html<html> <head> <meta http-equiv="Content-Type" context="text/html; charset=UTF-8" /> <meta charset="UTF-8" /> <title>JavaScriptでコラッツ予想</title> <script> function doCalc() { var txtN1 = document.getElementById("txtN1"); var n1, n2, strResult; var vWork = []; var vOut = []; n1 = parseInt(txtN1.value, 10); n2 = n1; vOut.push("" + n1); while (1) { if (n2 <= 1) { break; } if (n2 % 2 == 0) { n1 = n2 n2 = n2 / 2; strResult = "" + n1 + " / 2 = " + n2; vOut.push(strResult); } else { n1 = n2 n2 = n2 * 3 + 1; strResult = "" + n1 + " * 3 + 1 = " + n2; vOut.push(strResult); } } document.getElementById("result").innerHTML = vOut.join("\n"); } </script> </head> <body> <h2>コラッツ予想</h2> <ul> <li>自然数を入力する</li> <li>「偶数なら2で割り、奇数なら3倍して1を足す」という操作を繰り返す</li> <li>1になったら終了する</li> </ul> 4桁 まで <input type="text" id="txtN1" value="" maxlength="4" size="7" oninput="value = value.replace(/[^0-9]+/i,'');" /> <input type="button" value="計算" onclick="doCalc()" /><br /> <br /> <textarea id="result" rows="50" col="20" style="width:700px;height:400px;"> </textarea> </body> </html>
- 投稿日:2020-12-01T17:47:00+09:00
JSのお仕事 (JSアウトプット2)
あいさつ
初めての人は初めまして!知っている人はこんにちは!
中学生プログラマーのAtieです!
今回もJSを学んできたのでそのアウトプットをしていきます!
今回は第二回目ですがもし第一回の記事を読んでいなければそちらのほうから読んでいただくとわかりやすいと思います!なぜJSが必要なのか? (JS学習アウトプット)
今回は「JSのお仕事」ということでJSのお仕事(役割)について学んできました!
それでは!前回の復習
従来のweb
- レスポンスがhtml
- ページ全体がレンダリング現代のweb
- レスポンスがJSON
- 必要なところだけレンダリング
- リッチで使いやすいJSのお仕事
JSのお仕事には主に二つあります
一つ目がデータのやり取り
二つ目が
DOM操作
です
順に解説していきます
データのやり取り
ではまずJSの一つ目のお仕事「データのやり取り」についてです
Twitterを例に解説していきますまずはTwitterのhtmlの構造を見ていきましょう!(この構造とは限りませんし省略してあります)
home.html<ul> <li> <div> <img src="~"> <div>...</div> </div> </li> </ul>もし構造がこのようになっているとします
しかしこれだとスクロールすると新しいツイートが表示されないので
スクロールするとJSがwebAPIに「データが欲しいです!」と頼みに行きます
そしてwebAPIがDBなどにアクセスしてデータをとってきてJSに「ほれ!」とデータを渡します
そしてこの時に渡されるデータが「JSON」なのです
JSONについては前回学んだので説明は省きますそれでは渡されたJSONのデータを見てみましょう(これはあくまでも一例です本物のTwitterがこうだとは限りません)
data.json{ "data" : [ { "name": "~~", "bady": "~~", "src": "~~" }, { "name": "~~", "bady": "~~", "src": "~~" }, { "name": "~~", "bady": "~~", "src": "~~" } ] }このようなデータになっていました!
しかしデータを受け取ったところでこのまま表示させるわけにはいきません
読みにくいのでhtmlに変換しますこれをパースといいます
パース自体は「解析」という意味で何もhtmlに変換することすべてがパースと呼ぶわけではありません
パースについてですがここからはDOM操作ですDOM操作
さぁ!先ほどwebAPIからjsonをもらうことができました!
「わーい!」
...
しかしまだ仕事は残っています
先ほど説明しましたがjsonをhtmlに変換して見やすくします
これをパースと呼びますDOM操作の例を見るためにhtmlとjsonのデータを見てみます
home.js<li> <div> <img src="data[0].src"> <!--jsonデータの0番目のsrcのデータがここに入る--> <h1>data[0].name</h1> <!--jsonデータの0番目のnameのデータがここに入る--> <p>data[0].bady</p> <!--jsonデータの0番目のbadyのデータがここに入る--> </div> </li>data.json{ "data" : [ { "name": "~~", "bady": "~~", "src": "~~" }, { "name": "~~", "bady": "~~", "src": "~~" }, { "name": "~~", "bady": "~~", "src": "~~" } ] }htmlにはコメントを書いたのでhtmlファイルとjsonのデータを見比べてみてください
わかりやすいと思います
このようにhtmlをjsonに変換することをパースと呼びましたがこのようなことがDOM操作ですパースが終わることで新しいデータが読み込まれ新しい投稿が表示されます
まとめ
今回のまとめです!
JSのお仕事
- データのやり取り
- DOM操作データのやり取りではJSがwebAPIにデータを取りに行きます
DOM操作ではとってきたjsonデータをhtmlに変換します(パースします)
この二つが主なJSのお仕事ですデータのやり取りには
- 保存する
- ローカルに保存する
- cookie
なども含まれていますDOM操作では
- 追加
- 変更
- 削除
などもしています最後に
ここまで読んでいただきありがとうございました
次回は実際にコードを書いてみます!
それでは!また次回に!AtieのTwitter
しまぶーのIT大学さんのTwitter参考
【基礎から学ぶ JavaScript 入門 #2】Twitterを例にJavaScriptがどんな働きをするのか理解しよう【ヤフー出身エンジニアが教える初心者向けプログラミング講座】
- 投稿日:2020-12-01T17:28:02+09:00
【JS】配列やオブジェクトは比較できない?比較できる場合とできない場合。
配列やオブジェクトの要素同士を比較するときの注意点メモ。
個別に定義した場合は比較できない
配列やオブジェクトの中身の値が同じでも、等価式にするとfalseになる。
いずれの場合も見た目は完全に一致してるが、評価結果はことごとくfalseになる。
これは、各配列の要素を指すポインタが異なる場所を示しているため。
比較可能な場合
元の配列やオブジェクトの要素を参照した場合は比較が可能になる。
filterの応用
上記の結果とfilterを使うと、必要な要素のみ残したり、除外したりすることができる。
例えば除外する場合は以下のようになる。
▼配列の場合
▼オブジェクトの場合
配列やオブジェクトの要素をごっそりフィルタリングしたい場合は評価式の評価対象に注意が必要。
- 投稿日:2020-12-01T16:50:50+09:00
Kinx での JIT、そして MIR の話 ... Ruby だけでは勿体ないネ
この記事は「言語実装 Advent Calendar 2020」 21日目の記事です。
本記事は、主に JIT コンパイルに関する話題ですが、一般的なものではなく私の身の回りでやってることを中心にご紹介したいと思います。
はじめに
今回は、言語実装 Advent Calendar ということで 自作言語である Kinx における、最新の
native
実装状況、そして JIT ライブラリのお話、および JIT 関連の注目ライブラリ MIR に関してご紹介します。本記事は、以下で構成されています。
- JIT コンパイラのプラットフォーム依存性について
- JIT コンパイラの概要と課題。一般的には実行時にどこをコンパイルするかとかトレーシングがどうとかなのかもしれませんが、プラットフォーム依存も一つの問題です。Ruby は独特の方法で回避しましたが...
- Kinx Native - sljit による JIT コンパイル
native
というキーワードに関して元々は ここ(JIT Compile) でいくつか紹介していたのですが、その後、割とアップデートさせたので再度まとめ直してみました。- JIT ライブラリ - 手軽に JIT コンパイルを体験
- 直接アセンブラを使わないまでも、抽象化されたアセンブリを直接扱うためのライブラリを用意しています。その紹介と今後したいことなど。
- MIR 感想
- 注目の JIT コンパイラ基盤。Cコンパイラが付いている。Ruby の人たちはよく知ってるアレです。非常に楽しみなプロジェクトで、少し触ってみた感想などをご紹介。
しかし本業が忙しすぎて色々なことを進める時間が圧倒的に足りなくなっている今、いつできるのか...。ちょっと前まではもう少し時間が取れたのですが。まぁ個人的なサイドプロジェクトなので、焦らず気長に行きます。もちろん、もし少しでもご興味を持っていただけるのであれば、どなたでもどんな内容でも コントリビュート大歓迎 です!(Kinx の GitHub は こちら)
【ご参考】Kinx とは
Kinx の最初のコミットは 2019/11/28 のようです。約 1 年(本記事の公開は 2020/12/21 ですが、書き始めたのは 11/11)経ちました。さて、元々の動機は下記を参照していただくとして、キャッチフレーズがありますので例によってそこからスタートします。Here we go!
「見た目は JavaScript、頭脳(中身)は Ruby、(安定感は AC/DC)」 でお届けしているスクリプト言語 Kinx。ご存じない方は下記をご参照ください。
- 参考
- 最初の動機 ... スクリプト言語 KINX(ご紹介)
- 個別記事へのリンクは全てここに集約してあります。
- リポジトリ ... https://github.com/Kray-G/kinx
- Pull Request 等お待ちしております。
つい昨日(12/20)、「令和時代の基礎文法最速マスター Advent Calendar 2020」 20日目の記事として「Kinx 基礎文法最速マスター」がアップされましたので、そちらも合わせてご覧ください。
1. JIT コンパイラのプラットフォーム依存性について
あらためて、Just In Time、つまり まさにその場で コンパイルして実行する方式。ネイティブコード(機械語、マシン語1)にコンパイルして実行するため実行速度を追い求める人たちの間では 欲しくて欲しくてたまらない機能 の一つです。
ただし、普通は プロセッサーごとに個別に実装しないといけない。x64 だけ、とかならまだいいんだけど、どっかで ARM くらいはサポートすっかなー、とか、PowerPC は、SPARC?、とかになると、そもそもテストするのが大変です。
そして、同じ x64 でも Windows と Linux は違います。Microsoft x64 Calling Convention と System V Calling Convention という、いわゆる 呼出規約 というものが違っています。
Microsoft x64 / System V Calling Convention
そもそも関数呼び出しで使うレジスタが違います。これは読み替えればよいのだけれど。
引数 1 引数 2 引数 3 引数 4 引数 5 引数 6 ... and more Microsoft rcx
rdx
r8
r9
(stack) (stack) (stack) System V rdi
rsi
rdx
rcx
r8
r9
(stack) double 型の受け渡しにはどちらも SSE レジスタを使いますが、Microsoft x64 の場合は
xmm0
からxmm3
まで、System V の場合はxmm0
からxmm7
までとなっています。そして、以下のように Microsoft x64 の場合は位置によって使うレジスタが固定であるのに対し、System V では 位置がズレます。
引数 1 int
引数 2 double
引数 3 int
引数 4 double
Microsoft ecx
xmm1
r8d
xmm3
System V edi
xmm0
esi
xmm1
こういうのを考えてプラットフォームごとに JIT コンパイラを作るわけですが、こういうのを意識しても所詮 x64 の世界の内側だけの話であって ARM にすら対応できていない、という現状なのです。
Ruby 方式
そこで、Ruby は なんと C ソースを出力してコンパイルして実行するという大胆な方式 を採用しました。メリットは上記に書いた多くのプロセッサへの対応を 他人に丸投げ(いい意味で)できること、デメリットはファイル入出力やプロセス起動などのオーバーヘッドが大いに気になることと 別途コンパイラを要求する ところ。実行環境にコンパイラを要求するのは、まぁ Linux なら gcc があるし、とか思うけど Windows が問題 ですよね。普通の人にコンパイラをインストールして、とかなかなか言いづらい。しかも本体とコンパイラを合わせないとイケなかったり。
これをやるなら、私なら C コンパイラも含めてパッケージに入れて提供したい、と思う。LLVM とか使えるのあるしね。デカいけど。ちなみに、以前 Clang で JIT するやり方を以下に公開してみました。Clang/LLVM 8.0.0 の頃なので多少古いが、今でも役に立つかな?
x64 だけに絞っても、全部スタティックリンクすると約 35MB の実行ファイルが出来上がります。デカいぜ。
そして(多少)時は流れ...
...と、ここまでは以前も書いた内容とほぼ同じ。で、やはり誰かが計画するわけですね。C コンパイラの同梱。そのためのプロジェクトが MIR です。がっつり Ruby の JIT に組み込むことを計画してます。軽量な最適化機能付き(関数のインライン展開付き) JIT コンパイラです。さらに、十分抽象化された中間表現 を持ち、それによって 様々なプラットフォームへの対応を可能 にしています。そして、C コンパイラが付いているのが超絶に便利 です。Ruby JIT に使うので当然でしょう。ところが、実はそれは単純に C スクリプト環境としても最適ということを意味します。あぁ、これ欲しかったですよ。ただし、まだバグも色々ありますが。
ある意味、C 言語にトランスパイルする言語はこれで全て実行可能にすることもできる でしょう。新たに C 言語にトランスパイルする言語を実装しようとしたとき、その言語実装自体は 超簡単 になりそうです2。...いや、いろいろ難しいかもしれませんが。イメージで話しました。すみません。
元々 Kinx でも同様に一気にアセンブルするやり方を試したりできないかなー、と kcs という自作 C JIT コンパイラを使ってみようと思ってました。ただ、x64 だけなのと最適化はほぼ無いので、MIR のほうが断然良いですね。もちろん kcs は JIT 基盤にしようとした訳ではなく、単に C スクリプトを実現しようとしたものなので、そもそもの目的が違ってます。しかし MIR みたいなのが出てくるとなると彼ももうお払い箱ですね…。残念ですが。一応 kcs の紹介だけ(更新止まってます)。
MIR は Ruby JIT への組み込みが計画されていますが、それだけでは勿体ないですね。これほどフレキシブルでライトウェイトでありながら、かつ十分抽象化された IR レイヤを持つ MIT ライセンスの(C コンパイラを含む)JIT コンパイラは他に見つけられなかったので、Kinx でも活用できないか、他に何か良い使い方が無いか、夢が膨らみます。MIR については後述します。つーか、Clang/LLVM はとにかくデカいんです(くどいですが)。
2. Kinx Native - sljit による JIT コンパイル
さて、本題に戻りましょう。まずは Kinx の
native
からです。Kinx では、
native
というキーワードを使うことでネイティブ・コードにコンパイルして実行するという機能を持っています。いわゆるトレーシングとかは無しに、いきなりネイティブ・コードにコンパイルして実行させます。これは数値計算を主とする各種ベンチマークではそれなりに結構いい感じの結果を出してます。例えば「この記事」参照。...が、実用上、通常のプログラムの中ではイマイチ使いこなせていません(私自身が。アイタタタ)。まぁでも、他の言語にはあまりない Kinx の特長でもあるので、大切に育てていきたい機能ではあります。
さて、Kinx の
native
による JIT コンパイルでは、上記のプラットフォームに依存してしまうという問題に対し、sljit を採用することで回避しています。sljit はアセンブラを抽象化しており、その抽象化アセンブラで書くことでコード出力時に x64 や ARM などに合わせて出力ができるようになっています。その代わり、レジスタ数とかどのプロセッサーでも共通に使えるようにミニマムに合わせておかなければなりません。ちなみに、sljit のサポートアーキは以下の通りとのこと。
- Intel-x86 32
- AMD-x86 64
- ARM 32 (ARM-v5, ARM-v7 and Thumb2 instruction sets)
- ARM 64
- PowerPC 32
- PowerPC 64
- MIPS 32 (III, R1)
- MIPS 64 (III, R1)
- SPARC 32
ただし、x64 以外ではテストしていない & 64ビットしか想定していないので悪しからず。
具体的なソースコードとして、ネイティブ・コード用 IR 出力ルーチンは src/ast_native.c にあり、sljit を使ったネイティブ・コード用コンパイラは src/nir_compile.c にあります。
また、sljit 自体の使い方(と事例)は、先日「Kinx 実現技術 - JIT: sljit の使い方」に書いてみましたので、興味ある方はこちらも参照してみてください。
Native - 個別の実装状況
では、本題となるアップデート状況です。ここ(JIT Compile) での紹介の後、ちょっとずつ進化してます。
IR (Intermediate Representation)
native
ではネイティブ・コードに変換する上で扱いやすいように、スクリプト VM 用の IR と全く異なるコードを出力します。なぜなら例外の扱いとかコンパイル時点における型チェックの厳密さとかが全く異なるためで、それに合わせて違う体系にしています。今回、IR 出力時のレジスタ割り当て方式を変更したところ、必要なレジスタ数が減ってだいぶスッキリさせられました。また、余計なコードもだいぶ出さないようにすることもしました。
前回の例でいうと、
native nfib(n) { if (n < 3) return n; return nfib(n-2) + nfib(n-1); }これは以下のようになります。
nfib(1, 3): .L0 .L1 0: load r1, $(0,0) 1: lt r2 = r1, 3 2: jz .L4 .L2 3: load r1, $(0,0) 4: ret r1 .L3 .L4 5: load r1, $(0,0) 6: sub r2 = r1, 2 7: arg r2 8: call r1 = [rec-call]() 9: excpt r2 = check a: jz .L6 .L5 b: ret 0 .L6 c: load r2, $(0,0) d: sub r3 = r2, 1 e: arg r3 f: call r2 = [rec-call]() 10: excpt r3 = check 11: jz .L8 .L7 12: ret 0 .L8 13: add r3 = r1, r2 14: ret r3 .L9 15: ret 0以前に比べて使用するレジスタ数が 10 → 3 に削減。ライン数も 0x20 → 0x15 に削減。
例外処理
例外機能は以前の記事から変わっていません。なので、現時点でも以下の制限があります。
- スタックトレースは取得できない。
- オブジェクトがまだ十分使えないので、キャッチする例外オブジェクトは実質あまり使えない。例外のフローはきちんと動作する。
いやぁ、もう少し機が熟す必要がありますね。
型チェック
これも以前から変わっていません。関数の入口で引数の型をチェック。レキシカル変数をアクセスする場合もチェック。関数から返ってきたときにもチェック。型が期待するものと違ってたらすぐさま例外を上げます。
Switch-Case
Switch-Case は、以前の記事では未サポートでしたが、頑張ってサポートしました。最新版では以下の形で実装しています。
- Case の数に応じて 2 分探索と線形探索を切り替える。
- Case 条件は Integer のみ。
- ジャンプテーブル化は現在未サポート。
たぶんジャンプテーブルも実現可能な感じには sljit をハックしたのですが、まずは 2 分探索で十分だろうと判断して上記で実現。少なくともこれで実用的な Switch-Case を実現することはできるので。ジャンプテーブル化は時間があれば実施しますが、優先度は低いです。
VM 関数呼び出し
これは現時点でも未サポートです。子 VM を起動してジャンプすれば良いとは思いますが、入れ子になった VM 内で例外が発生した場合の処理が大変そうなので、まだ手がつけられていません。おそらく以下のような感じでできるのでは、と思っています。
- フレームに
native
からのコールであるフラグを立てる。return
時、native
からであれば復帰値をセットして VM からreturn
する。- 例外発生時、スタックを巻き戻している最中に
native
コールされたフラグを見つけた場合、そこでスタックトレース情報の作成を中断し、native
向けの例外情報を設定してnative
にリターン。native
側に帰ってきたら、native
での例外伝搬の仕組みで伝搬させていく。例外のスタックトレースを
native
関数内でも保存するようにしないといけません。やってみる時間があれば挑戦してみる、という予定ですが、今は難しい。ただ、たぶん面倒くさいだけで実現可能だとは思います。オブジェクトへのアクセス
これは一部実装しました。具体的には
Integer
とDouble
への配列をサポートしました。以下のように Quick Sort くらいはできます。native quicksort_n(a:int[], first, last) { var i = first; var j = last; var x = a[(first + last) / 2]; while (true) { while (a[i] < x) i++; while (x < a[j]) j--; if (i >= j) break; [a[i], a[j]] = [a[j], a[i]]; ++i; --j; } if (first < i - 1) quicksort_n(a, first, i - 1); if (j + 1 < last) quicksort_n(a, j + 1, last); } const N = 20; var a = N.times { => Integer.parseInt(Math.random() * 100) }; System.print("Before:"); a.each { System.print(" %2d" % _1); }; System.print("\n"); quicksort_n(a, 0, N - 1); System.print("After: "); a.each { System.print(" %2d" % _1); }; System.print("\n");結果。
Before: 97 6 27 1 21 25 96 52 6 0 11 98 97 66 76 66 69 99 68 80 After: 0 1 6 6 11 21 25 27 52 66 66 68 69 76 80 96 97 97 98 99整数配列は
a:int[]
と記述し、実数の場合はa:dbl[]
という記述をします。レジスタ割り付け
実際には変数へのレジスタ割り付け自体はやっていません。
...が、IR 出力時のレジスタ割り当て方式を 大幅に 変更したので、それに伴って演算結果で保存可能なものをレジスタに割り当てることにした結果、劇的にメモリアクセスが減りました。例えば、上記 fib の例でいうと以下のようになります。
以前のコードでは
fib(n-2)
の部分は以下のように出力されていました(無駄なコードがいっぱいです)。d7: 48 8b 14 24 mov rdx, [rsp] db: 48 89 94 24 30 02 00 00 mov [rsp+0x230], rdx e3: 48 8b 94 24 30 02 00 00 mov rdx, [rsp+0x230] eb: 48 83 ea 02 sub rdx, 0x02 ef: 48 89 94 24 38 02 00 00 mov [rsp+0x238], rdx f7: 48 8b 94 24 38 02 00 00 mov rdx, [rsp+0x238] ff: 48 89 54 24 18 mov [rsp+0x18], rdx 104: 48 c7 84 24 18 01 00 00 01 00 00 00 mov [rsp+0x118], 0x01 112: 48 8b 4b 18 mov rcx, [rbx+0x18] 116: 48 89 d8 mov rax, rbx 119: 49 8b 57 08 mov rdx, [r15+0x8] 11d: 48 89 54 24 10 mov [rsp+0x10], rdx 122: 48 8d 74 24 08 lea rsi, [rsp+0x8] 127: 48 89 c7 mov rdi, rax 12a: ff d1 call rcx 12c: 48 89 84 24 40 02 00 00 mov [rsp+0x240], raxこれがこうなりました。
93: 48 8b 2c 24 mov rbp, [rsp] 97: 48 8d 45 fe lea rax, [rbp-0x2] 9b: 48 89 44 24 18 mov [rsp+0x18], rax a0: 48 c7 84 24 18 01 00 00 01 00 00 00 mov qword [rsp+0x118], 0x1 ac: 48 8b 4b 18 mov rcx, [rbx+0x18] b0: 48 89 d8 mov rax, rbx b3: 49 8b 57 08 mov rdx, [r15+0x8] b7: 48 89 54 24 10 mov [rsp+0x10], rdx bc: 48 8d 74 24 08 lea rsi, [rsp+0x8] c1: 48 89 c7 mov rdi, rax c4: ff d1 call rcx c6: 48 89 c5 mov rbp, rax分かりづらいとは思いますが全部載せるとこんな感じで、メモリアクセスがかなり減りました。
nfib: (native-base:0x7fbd9c030010) 0: 53 push rbx 1: 41 57 push r15 3: 41 56 push r14 5: 41 55 push r13 7: 55 push rbp 8: 41 54 push r12 a: 48 8b df mov rbx, rdi d: 4c 8b fe mov r15, rsi 10: 4c 8b f2 mov r14, rdx 13: 48 81 ec 38 02 00 00 sub rsp, 0x238 1a: 49 8b 47 08 mov rax, [r15+0x8] 1e: 48 83 c0 01 add rax, 0x1 22: 49 89 47 08 mov [r15+0x8], rax 26: 48 3d 00 04 00 00 cmp rax, 0x400 2c: 72 29 jb 0x57 2e: 48 c7 43 20 01 00 00 00 mov qword [rbx+0x20], 0x1 36: 48 c7 43 28 06 00 00 00 mov qword [rbx+0x28], 0x6 3e: 48 c7 c0 00 00 00 00 mov rax, 0x0 45: 48 81 c4 38 02 00 00 add rsp, 0x238 4c: 41 5c pop r12 4e: 5d pop rbp 4f: 41 5d pop r13 51: 41 5e pop r14 53: 41 5f pop r15 55: 5b pop rbx 56: c3 ret 57: 49 83 bf 10 01 00 00 01 cmp qword [r15+0x110], 0x1 5f: 0f 85 11 01 00 00 jnz 0x176 65: 49 8b 57 10 mov rdx, [r15+0x10] 69: 48 89 14 24 mov [rsp], rdx 6d: 48 8b 2c 24 mov rbp, [rsp] 71: 48 89 e8 mov rax, rbp 74: 48 83 f8 03 cmp rax, 0x3 78: 7d 19 jge 0x93 7a: 48 8b 2c 24 mov rbp, [rsp] 7e: 48 89 e8 mov rax, rbp 81: 48 81 c4 38 02 00 00 add rsp, 0x238 88: 41 5c pop r12 8a: 5d pop rbp 8b: 41 5d pop r13 8d: 41 5e pop r14 8f: 41 5f pop r15 91: 5b pop rbx 92: c3 ret 93: 48 8b 2c 24 mov rbp, [rsp] 97: 48 8d 45 fe lea rax, [rbp-0x2] 9b: 48 89 44 24 18 mov [rsp+0x18], rax a0: 48 c7 84 24 18 01 00 00 01 00 00 00 mov qword [rsp+0x118], 0x1 ac: 48 8b 4b 18 mov rcx, [rbx+0x18] b0: 48 89 d8 mov rax, rbx b3: 49 8b 57 08 mov rdx, [r15+0x8] b7: 48 89 54 24 10 mov [rsp+0x10], rdx bc: 48 8d 74 24 08 lea rsi, [rsp+0x8] c1: 48 89 c7 mov rdi, rax c4: ff d1 call rcx c6: 48 89 c5 mov rbp, rax c9: 48 8b 43 20 mov rax, [rbx+0x20] cd: 48 83 f8 00 cmp rax, 0x0 d1: 74 19 jz 0xec d3: 48 c7 c0 00 00 00 00 mov rax, 0x0 da: 48 81 c4 38 02 00 00 add rsp, 0x238 e1: 41 5c pop r12 e3: 5d pop rbp e4: 41 5d pop r13 e6: 41 5e pop r14 e8: 41 5f pop r15 ea: 5b pop rbx eb: c3 ret ec: 4c 8b 24 24 mov r12, [rsp] f0: 49 8d 44 24 ff lea rax, [r12-0x1] f5: 48 89 44 24 18 mov [rsp+0x18], rax fa: 48 c7 84 24 18 01 00 00 01 00 00 00 mov qword [rsp+0x118], 0x1 106: 48 8b 4b 18 mov rcx, [rbx+0x18] 10a: 48 89 d8 mov rax, rbx 10d: 49 8b 57 08 mov rdx, [r15+0x8] 111: 48 89 54 24 10 mov [rsp+0x10], rdx 116: 48 8d 74 24 08 lea rsi, [rsp+0x8] 11b: 48 89 c7 mov rdi, rax 11e: ff d1 call rcx 120: 49 89 c4 mov r12, rax 123: 48 8b 43 20 mov rax, [rbx+0x20] 127: 48 83 f8 00 cmp rax, 0x0 12b: 74 19 jz 0x146 12d: 48 c7 c0 00 00 00 00 mov rax, 0x0 134: 48 81 c4 38 02 00 00 add rsp, 0x238 13b: 41 5c pop r12 13d: 5d pop rbp 13e: 41 5d pop r13 140: 41 5e pop r14 142: 41 5f pop r15 144: 5b pop rbx 145: c3 ret 146: 4a 8d 44 25 00 lea rax, [rbp+r12] 14b: 48 81 c4 38 02 00 00 add rsp, 0x238 152: 41 5c pop r12 154: 5d pop rbp 155: 41 5d pop r13 157: 41 5e pop r14 159: 41 5f pop r15 15b: 5b pop rbx 15c: c3 ret 15d: 48 c7 c0 00 00 00 00 mov rax, 0x0 164: 48 81 c4 38 02 00 00 add rsp, 0x238 16b: 41 5c pop r12 16d: 5d pop rbp 16e: 41 5d pop r13 170: 41 5e pop r14 172: 41 5f pop r15 174: 5b pop rbx 175: c3 ret 176: 48 c7 43 20 01 00 00 00 mov qword [rbx+0x20], 0x1 17e: 48 c7 43 28 07 00 00 00 mov qword [rbx+0x28], 0x7 186: 48 c7 c0 00 00 00 00 mov rax, 0x0 18d: 48 81 c4 38 02 00 00 add rsp, 0x238 194: 41 5c pop r12 196: 5d pop rbp 197: 41 5d pop r13 199: 41 5e pop r14 19b: 41 5f pop r15 19d: 5b pop rbx 19e: c3 ret最適化
定数伝搬と定数の畳み込み を実装しました。他はやっていませんが、
enum
とかconst
とかしてシンボル化した値を伝搬させて畳み込むのは結構効果があります。Kinx Native - 今後の予定
以下はやりたいですが、全然取り掛かれる気配がないのが問題ですね。やはり焦らず気長に。。。
- オブジェクトの配列
- スクリプト関数の呼び出し
- オブジェクト・プロパティアクセス
- BigInteger のオブジェクト利用効率化
- 例外オブジェクトの送出
3. JIT ライブラリ - 手軽に JIT コンパイルを体験
さて、次は JIT ライブラリです。
Kinx では、
native
以外にも直接 JIT を楽しめる JIT ライブラリがあります。これはまさしく sljit をラップしたライブラリです(sljit の使い方の例等は「こちら」)。必要なレジスタ数などの自動計算などもあり、直接使うより便利にはしています。JIT ライブラリに関しては以下に記事を載せています。私個人的にはこれを拡張して色々できると楽しいなー、と思っていますが、なかなか手が付けられませんね。うーん。
以前と同じことを書いても仕方がないので、サンプルを再掲して今後したいことなど。細かい使い方に関しては上記の「JIT ライブラリ」をご参照ください。
JIT ライブラリ・サンプル
こんな感じで、抽象化アセンブラを Kinx 上から手軽に使えます。
using Jit; var c = new Jit.Compiler(); var entry1 = c.enter(); var jump0 = c.ge(Jit.S0, Jit.IMM(3)); c.ret(Jit.S0); var l1 = c.label(); c.sub(Jit.R0, Jit.S0, Jit.IMM(2)); c.call(entry1); c.mov(Jit.S1, Jit.R0); c.sub(Jit.R0, Jit.S0, Jit.IMM(1)); c.call(entry1); c.add(Jit.R0, Jit.R0, Jit.S1); c.ret(Jit.R0); jump0.setLabel(l1); var code = c.generate(); for (var i = 1; i <= 42; ++i) { var tmr = new SystemTimer(); var r = code.run(i); System.println("[%8.3f] fib(%2d) = %d" % tmr.elapsed() % i % r); }
Jit.Compiler
オブジェクトを作って、enter
で関数エントリを作り、色々レジスタをいじくりまわしてret
するコードを書きます。で、実行するときはgenerate()
してrun()
、となります。generate()
してdump()
とすると、アセンブル・リストを見ることもできます。上記は、よく見ないと分からないかもしれませんが(ご想像の通り)フィボナッチ数列を求めるプログラムです。アセンブル・リストを出してみましょう。実行せずに
code.dump()
とすると以下が表示されます。0: 53 push rbx 1: 41 57 push r15 3: 41 56 push r14 5: 48 8b df mov rbx, rdi 8: 4c 8b fe mov r15, rsi b: 4c 8b f2 mov r14, rdx e: 48 83 ec 10 sub rsp, 0x10 12: 48 83 fb 03 cmp rbx, 0x3 16: 73 0d jae 0x25 18: 48 89 d8 mov rax, rbx 1b: 48 83 c4 10 add rsp, 0x10 1f: 41 5e pop r14 21: 41 5f pop r15 23: 5b pop rbx 24: c3 ret 25: 48 8d 43 fe lea rax, [rbx-0x2] 29: 48 89 fa mov rdx, rdi 2c: 48 89 c7 mov rdi, rax 2f: e8 cc ff ff ff call 0x0 34: 49 89 c7 mov r15, rax 37: 48 8d 43 ff lea rax, [rbx-0x1] 3b: 48 89 fa mov rdx, rdi 3e: 48 89 c7 mov rdi, rax 41: e8 ba ff ff ff call 0x0 46: 49 03 c7 add rax, r15 49: 48 83 c4 10 add rsp, 0x10 4d: 41 5e pop r14 4f: 41 5f pop r15 51: 5b pop rbx 52: c3 retここでは Linux ですが、Windows だと使われるレジスタが変わります。そう、例の呼出規約の話です。型チェックなどがバッサリ無い分、そして書いた通りのコードが出ているので(大体のところ)非常に綺麗ですね。スタックの操作とかレジスタの退避とかは必要な分だけ自動的に行われるので、その辺りもラクチンです。
JIT ライブラリ・ベンチマーク
では、ベンチマークを載せてみましょう。fib(42) を再帰で計算させた結果です。
言語 版数 User 時間 Kinx(Jit-Lib) 0.10.0 0.828 HHVM 3.21.0 2.227 Kinx(native) 0.10.0 2.250 PyPy 5.10.0 3.313 PHP 7.2.24 11.422 Ruby 2.5.1p57 14.877 Kinx 0.10.0 27.478 Python 2.7.15+ 41.125 JIT ライブラリは結構使い方次第で役に立ちそうだとは思ってはいるんですけどね。何気に
native
もいい結果なので、目下、自己満足中です。しかし Ruby 速いなー。どんどん速くなるなー。いいナー3。それ以上に PHP が速いけど。JIT ライブラリ・今後の予定
そんな JIT ライブラリとしては、以下を計画したいと思っています。
- 文字列操作機能(Kinx オブジェクトも使用可能かつある程度抽象化した形で)。
- C 関数のお手軽呼び出し機能。
C 関数をお手軽に呼べるようにできれば、かなり拡張できるようになります。具体的には dll からエントリ・ポイントを取得してコールするようにすれば良さそうなので、やればできそうです。
あー、やりたいことは沢山あります。焦らず、気長に。
4. MIR 感想
そしてお待ちかねの MIR です。Windows でも Linux でもそれなりには行けました。ここでは Linux での内容をお伝えします。オリジナルのリポジトリは以下です。
Build してみよう
では早速 MIR にトライしてみましょう。clone します。
$ git clone https://github.com/vnmakarov/mir.git Cloning into 'mir'... remote: Enumerating objects: 435, done. remote: Counting objects: 100% (435/435), done. remote: Compressing objects: 100% (231/231), done. remote: Total 10386 (delta 280), reused 336 (delta 204), pack-reused 9951 Receiving objects: 100% (10386/10386), 3.59 MiB | 3.08 MiB/s, done. Resolving deltas: 100% (6372/6372), done. Checking out files: 100% (1370/1370), done. $ cd mir $ ls CMakeLists.txt c2mir mir-gen-ppc64.c mir-interp.c mir.h HOW-TO-PORT-MIR.md check-threads.sh mir-gen-s390x.c mir-ppc64.c mir2c LICENSE include mir-gen-stub.c mir-reduce.h mir3.svg MIR.md llvm2mir mir-gen-x86_64.c mir-s390x.c mirall.svg Makefile mir-aarch64.c mir-gen.c mir-tests real-time.h README.md mir-bin-driver.c mir-gen.h mir-utils sieve.c adt-tests mir-bitmap.h mir-gen.svg mir-varr.h c-benchmarks mir-dlist.h mir-hash.h mir-x86_64.c c-tests mir-gen-aarch64.c mir-htab.h mir.ccmake がありますが、Makefile がありますので Linux(実際は WSL)では直接 make します。
$ make cc -std=gnu11 -Wno-abi -fsigned-char -fno-tree-sra -fno-ipa-cp-clone -DMIR_PARALLEL_GEN -c -O3 -g -DNDEBUG -o mir.o mir.c cc -std=gnu11 -Wno-abi -fsigned-char -fno-tree-sra -fno-ipa-cp-clone -DMIR_PARALLEL_GEN -c -O3 -g -DNDEBUG -o mir-gen.o mir-gen.c cc -std=gnu11 -Wno-abi -fsigned-char -fno-tree-sra -fno-ipa-cp-clone -DMIR_PARALLEL_GEN -O3 -g -DNDEBUG -I. mir-gen.o c2mir/c2mir.c c2mir/c2mir-driver.c mir.o -lm -ldl -lpthread -o c2m cc -std=gnu11 -Wno-abi -fsigned-char -fno-tree-sra -fno-ipa-cp-clone -DMIR_PARALLEL_GEN -I. -O3 -g -DNDEBUG -o m2b mir.o mir-utils/m2b.c cc -std=gnu11 -Wno-abi -fsigned-char -fno-tree-sra -fno-ipa-cp-clone -DMIR_PARALLEL_GEN -I. -O3 -g -DNDEBUG -o b2m mir.o mir-utils/b2m.c cc -std=gnu11 -Wno-abi -fsigned-char -fno-tree-sra -fno-ipa-cp-clone -DMIR_PARALLEL_GEN -I. -O3 -g -DNDEBUG -o b2ctab mir.o mir-utils/b2ctab.cあっさりと、うまくいきました。
ベンチマークしてみよう
では恒例のフィボナッチ、行ってみましょう。JIT ライブラリのときと同じ条件で fib(42) で実行し、結果をマージしてみます。
int printf(const char *fmt, ...); #define N 42 int fib(int n) { if (n < 3) return n; return fib(n-2) + fib(n-1); } int main(void) { printf("fib %d = %d\n", N, fib(N)); return 0; }gcc と比較してみるため、gcc でもコンパイル。
$ gcc -O0 -o fib_gcc_o0 fib.c $ gcc -O2 -o fib_gcc_o2 fib.cさて、結果です。
言語 版数 User 時間 備考 GCC -O2 7.5.0 0.531 コンパイル時間を除く Kinx(Jit-Lib) 0.10.0 0.828 GCC -O0 7.5.0 1.109 コンパイル時間を除く c2m -eg - 1.500 全ての関数を最初にコンパイルする方式 c2m -el - 1.750 関数が呼び出される直前にコンパイル、必要な関数のみコンパイル HHVM 3.21.0 2.227 Kinx(native) 0.10.0 2.250 PyPy 5.10.0 3.313 PHP 7.2.24 11.422 Ruby 2.5.1p57 14.877 Kinx 0.10.0 27.478 Python 2.7.15+ 41.125 c2m -ei - 71.453 全て MIR インタプリタで実行 さすがに
GCC -O2
が速いです。といってもコンパイルが無視されていますがこの程度だと 15ms くらいだったのでまぁ良いでしょう。今のところ、GCC の最適化無しでも c2m より速いようですが、コンパイル時間を除くと若干差は縮まるとは思われます。尚、c2m は-O2
がデフォルトのようです。-O0
を付けると遅くなります。元々、gcc とか clang とかはスタートアップとコンパイルフェーズが長いのでそれを回避したい、ということなので、ランタイムは多少遅くても良いでしょう。そういう意味では gcc とは比較したい場所が違いますね。この MIR、まだプロジェクトは初期のステージとのことなのでこれからと言ったところでしょう。現時点での結果としては、全然悪くないです。
IR 自体まだ仕様が安定してなさそうなのですが、非常に楽しみです。
(その他)MIR での標準ライブラリについて
printf
を自分でプロトタイプ宣言してますが、MIR では標準ライブラリを 動的にロードして 実行していました。おお、画期的。こんなやり方できたんですね。目からうろこでした。実際本当にいいのかはよくわかりませんが、このやり方でうまく動いています。具体的には以下のソースコードです(リンク先が変わってしまったらごめんなさい)。
- https://github.com/vnmakarov/mir/blob/master/c2mir/c2mir-driver.c
std_libs
配列に標準ライブラリの dll/so ファイルのパスを入れておき、dlopen
等で動的にエントリ・ポイントを取得。この技、今度使おう。
今後
いくつか妄想だけでも...
- Ruby 方式の簡易版 JIT コンパイルを Kinx で実現してみたり。
- C 拡張スクリプトとして色んな言語の拡張を C の動的コンパイルで実現できるようにしてみたり。
- 簡単なサンプル自作言語として C へのトランスパイラを作成してみたり。
時間がいくらあっても足りない。
と、いいつつ、MIR を使いたい一心で何か始めます。というか始めてます。ちゃんと記事ができるようになったら紹介できるかもしれません。
おわりに
ここまで読んでくださってありがとうございます。
今後、以下のようなことは考えています。
言語実装 Advent Calendar ということで、自作言語 Kinx を紹介させていただくと共に、それに関する JIT 関連と最近の興味を記事にしてみました。全部と言わずとも少しでも皆さんの興味にヒットして、何かしらのお役に立てれば幸いです。
また、もし宜しければ Kinx は GitHub にソースコードを公開していますので、Star とか貰えるとやる気出ます(やる気があっても時間がないのはいかんともしがたいが...)。泣き言言ってますが、焦らず、気長に見守っててください。もちろん、最初にも書きましたが、どなたでもどんな内容でも コントリビュート大歓迎 ですので、何かありましたらぜひぜひ宜しくお願い致します。
ではこの辺で、またどこかでお会いしましょう。
改めて、最後までお読みいただき、ありがとうございました。
全く関係ない話ですが、昔、何かの本で「マシン語は一部で『魔神語』と呼ばれ恐れられている」(←うろ覚え、適当)みたいなことが書いてあって、子供ながらに面白いと思った記憶が多少残ってます(なんだっけなー)。 ↩
当時(Kinx を作り始めたのは約 1 年前)、これの完成版があったら、もしかしたら最初から全て C 言語にトランスコンパイルする形で設計したかもしれません。というより、Kinx 以前に同じシンタックスで作っていた前身言語は、Kinx 同様 VM 実行させるように実装した後、Clang ベースで別バージョンを作った過去があります。当然のように起動がモッサリしました。MIR はこういうのを避けられそうで Good です。 ↩
ちなみに Kinx は Ruby 2.4.0 と大体同じくらいの実行速度なレベルです。VM 性能は、割とがっつりチューニングしているのでそんなに変わらん気がするのだが。GC の差かな。あまり頑張って調べる気はないのですけど。Kinx の GC は当初から変わらず Stop The World での Mark & Sweep を堅持しているので。しかし、Ruby は1.8.7 の頃とは全く比較になりませんね。 ↩
Kinx 自体はデバッガ作りたいとか、Language Server をサポートしたい(主に VSCode)とか、パッケージマネージャーを用意したいとか、マニュアルをコンプリートさせたいとか、GUI ライブラリ取り揃えたいとか、やりたいことはテンコ盛りなのですが…。 ↩
- 投稿日:2020-12-01T16:47:30+09:00
【JS】関数を代入した変数を複数記述するときの注意点
Booleanを返す関数を代入した変数の処理で、複数の条件(
&&
や||
)を設定する場合の記述の注意点メモ。
正しい記述
複数の真偽値を設定する場合は、以下のように、引数を明示して双方の変数にそれぞれ渡す必要がある。
OK事例moreThanOne = ({arr}) => arr.length > 1 lessThanThree = ({arr}) => arr.length < 3 //正常な記述 judgeOK = (params) => lessThanThree(params) && moreThanOne(params)
これを、引数を記述せず、変数をつなげると意図しない結果になってしまう。NG事例//間違った記述 judgeNG = lessThanThree && moreThanOne
なぜ意図しない結果になってしまうのか?
NG事例judgeNG = lessThanThree && moreThanOneこの式は、lessTanThreeの結果と、moreThanOneの結果を&&で結んでいるのではなく、 変数自体をつなげている事になる。
&&は前者がtrue / false以外の場合、後方の値を返す。
このため、上記式は
judgeNG = moreThanOne
と同じになってしまう。▼確認用コード
arr = [1,2,3,4,5] moreThanOne = ({arr}) => arr.length > 1 lessThanThree = ({arr}) => arr.length < 3 //意図しない動き judgeNG = lessThanThree && moreThanOne console.log( judge({arr}) ) //true //意図した動き judgeOK = (params) => lessThanThree(params) && moreThanOne(params) console.log( judge({arr}) ) //false上記のように、引数を記述しない場合は、後方のmoreThanOneのみが評価され、lessThanThreeが無視された結果になってしまう。
一方で引数を記述した場合は、
true && false = false
が返る。
paramsとは何か?
paramsは渡された引数全てを表している。引数名は任意なので、params以外でも問題ない。
arr = [1,2,3,4,5] moreThanOne = ({arr}) => arr.length > 1 lessThanThree = ({arr}) => arr.length < 3 judge = (o) => lessThanThree(o) && moreThanOne(o) console.log( judge({arr}) ) //false上記の場合、
o
には{arr}
が入っている。
変数が一つの場合
変数が1つの場合引数を渡さずとも意図した動きになるため、これが混乱する要因にもなっていた、、
moreThanOne = ({arr}) => arr.length > 1 //意図した動きになる judge = moreThanOne judge({arr})これは、judgeがmorethanOneに格納された変数そのものになっているため、引数を渡さずとも真偽値が返る。
judge = moreThanOne
↓↑
judge = ({arr}) => arr.length > 1
- 投稿日:2020-12-01T16:12:52+09:00
【入門者向け】Canvas入門講座#15 モザイクしてみよう【JavaScript】
問題15
問題13~15は問題9がベースとなっています。まず問題9を実装してください。
画像読み込み後、mosaic押下時に8x8のモザイク処理を行え。
モザイク処理とは平均の色で塗りつぶすことである。以下のHTMLを使用すること。(今回の問題はcanvasのサイズが640x320になっています。)
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <title>問題15</title> <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script> <script> $(() => { $('#my-file').on('change', e => { const img = new Image(); $(img).on('load', e => { }); img.src = URL.createObjectURL(e.target.files[0]); }); $('#mosaic-button').click(e => { }); }); </script> </head> <body> <canvas id="my-canvas" width="640" height="320"></canvas> <br> <input id="my-file" type="file" /> <br> <input id="mosaic-button" type="button" value="mosaic" /> </body> </html>解答
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <title>問題15</title> <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script> <script> $(() => { const MOSAIC_WIDTH = 8, MOSAIC_HEIGHT = 8; $('#my-file').on('change', e => { const img = new Image(); $(img).on('load', e => { // コンテキストを取得 const ctx = $('#my-canvas')[0].getContext('2d'); // canvasを黒色で塗りつぶす ctx.fillStyle = '#000000'; ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height); // canvasと画像のアスペクト比を求める const canvasWidth = $('#my-canvas').prop('width'), canvasHeight = $('#my-canvas').prop('height'); canvasAspect = canvasWidth / canvasHeight; // canvasのアスペクト比 imgAspect = img.width / img.height; // imgのアスペクト比 // canvasと画像のアスペクト比を比較し、貼り付ける領域を決定する let dstWidth, dstHeight; if(canvasAspect > imgAspect) {// canvasの方が横長 dstHeight = canvasHeight; dstWidth = dstHeight * imgAspect; } else {// canvasの方が縦長 dstWidth = canvasWidth; dstHeight = dstWidth / imgAspect; } // 画像を貼り付ける ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, dstWidth, dstHeight); }); img.src = URL.createObjectURL(e.target.files[0]); }); $('#mosaic-button').click(e => { const canvas = $('#my-canvas')[0], ctx = canvas.getContext('2d'); ctx.save(); const imgData = ctx.getImageData(0, 0, canvas.width, canvas.height), data = imgData.data; for(let y = 0; y < canvas.height; y += MOSAIC_HEIGHT) { for(let x = 0; x < canvas.width; x += MOSAIC_WIDTH) { // モザイクの色を計算する let r, g, b; r = g = b = 0; for(let j = 0; j < MOSAIC_HEIGHT; j += 1) { for(let i = 0; i < MOSAIC_WIDTH; i += 1) { // 画素のインデックスを求める const p = ((y + j) * canvas.width + (x + i)) * 4; r += data[p]; g += data[p + 1]; b += data[p + 2]; } } // 各色の平均を計算する const pixelCount = MOSAIC_WIDTH * MOSAIC_HEIGHT; // モザイクの画素数 r /= pixelCount; g /= pixelCount; b /= pixelCount; // モザイクをかける for(let j = 0; j < MOSAIC_HEIGHT; j += 1) { for(let i = 0; i < MOSAIC_WIDTH; i += 1) { // 画素のインデックスを求める const p = ((y + j) * canvas.width + (x + i)) * 4; data[p] = r; data[p + 1] = g; data[p + 2] = b; } } } } // imageDataをcanvasに貼り付ける ctx.putImageData(imgData, 0, 0); ctx.restore(); }); }); </script> </head> <body> <canvas id="my-canvas" width="640" height="320"></canvas> <br> <input id="my-file" type="file" /> <br> <input id="mosaic-button" type="button" value="mosaic" /> </body> </html>モザイク処理ができました。かっこいいですね。
解説
基本的な流れは問題13,14と似ています。
やりたいことは8x8の平均の色で塗りつぶすことです。グレースケールや2値化では1つのfor文で回していましたが
今回のモザイク処理ではx方向とy方向の2つのfor文でループさせたほうがわかりやすいと思います。
下図のようにYの値を8ずつ変えながらX方向に8ずつ走査します。for(let y = 0; y < canvas.height; y += MOSAIC_HEIGHT) { for(let x = 0; x < canvas.width; x += MOSAIC_WIDTH) { // ここでモザイク処理 } }今回のモザイクサイズは8x8ですので、
現在の画素からさらにx方向とy方向の2つのfor文でループさせて、まずは画素の各成分を全部足し合わせます。
それをモザイクの画素数で割れば平均が出ます。
その平均の色で塗りつぶします。// モザイクの色を計算する let r, g, b; r = g = b = 0; for(let j = 0; j < MOSAIC_HEIGHT; j += 1) { for(let i = 0; i < MOSAIC_WIDTH; i += 1) { // 画素のインデックスを求める const p = ((y + j) * canvas.width + (x + i)) * 4; r += data[p]; g += data[p + 1]; b += data[p + 2]; } } // 各色の平均を計算する const pixelCount = MOSAIC_WIDTH * MOSAIC_HEIGHT; // モザイクの画素数 r /= pixelCount; g /= pixelCount; b /= pixelCount; // モザイクをかける for(let j = 0; j < MOSAIC_HEIGHT; j += 1) { for(let i = 0; i < MOSAIC_WIDTH; i += 1) { // 画素のインデックスを求める const p = ((y + j) * canvas.width + (x + i)) * 4; data[p] = r; data[p + 1] = g; data[p + 2] = b; } }発展
今回canvasのサイズが640x320で幅、長さ共に8で割り切れましたが
割り切れない場合はどのように実装すればよいでしょうか?
よい練習になると思うのでぜひやってみてください。(こちらが参考になるかも)
- 投稿日:2020-12-01T16:05:22+09:00
人物画像の姿勢推定をNode-REDで行う方法
日立製作所OSSソリューションセンタの横井です。前の記事では、RedHat OpenShiftのNode-REDオペレータの使用方法をご紹介しました。この記事では、OpenShiftのNode-RED環境と、TensorFlowコンテナを接続する手順についてご説明します。
使用例
ここでは、自動車を運転する際の警報システムを実現するNode-REDのフローの例を使用します。日本では、ご存知のように自動車は左側の道路を走り、サイクリストもその自動車の同じ左側を走ります。そのため、サイクリストが自動車に近づく際は、ドライバーは衝突を回避する必要があります。この状況を支援するため、このシステムは、サイクリストが右折する兆候を示したことを人間の姿勢推定モデルを使用して判定し、ドライバーへ警告を表示します。
次の手順を行う前に、前の記事で説明したようにNode-REDオペレータを使用して、事前にNode-RED環境を作成する必要があります。すべての操作は、OpenShiftウェブコンソール上で、adminロールではなく、developerアカウントで行う必要があります。
(1) TensorFlowコンテナのデプロイ
ウェブコンソール上で、「+Add」->「Container image」の項目へアクセスします。
次のウィザードでは、Image name from external registryに「quay.io/codait/max-human-pose-estimator」を貼り付けます。このコンテナには、入力画像から人間の姿勢を推定するREST API サーバが含まれています(興味がある場合は、詳細を参照してください)。Generalフィールドの"application name"と"name"の他の項目については、イメージ名を貼り付けた後、自動的に入力されます。
ウィザードのCreateボタンをクリックしてから約2分が経過すると、max-human-pose-estimatorインスタンスのリングが青になり、デプロイが成功したことが分かります。
OpenShiftのロゴが表示されているmax-human-pose-estimatorインスタンスをダブルクリックすることによって、詳細サイドエリアのResourcesタブのRoutesフィールドの中にREST APIのエンドポイントURLが生成されていることを確認できます。
エンドポイントのURLにアクセスすると、Swagger UIからREST APIサーバに用意されているメソッドを呼び出すことができます。最後に、Node-REDのフローを編集する時に用いるため、このエンドポイントのURLを覚えておいてください。
(2) Node-REDのフローを読み込み
GitHubからサンプルフローを読み込むには、まずNode-REDフローエディタのメニューで「プロジェクト」->「新規」を選択します。Node-REDオペレータによって作成されたNode-RED環境では、このプロジェクト機能と呼ばれるGitHubとの統合機能がデフォルトで有効になっています。
以下のウィザードで「リポジトリのクローン」ボタンをクリックして、サンプルのNode-REDフローを含むリポジトリを複製します。
次のウィンドウでは、gitコマンドが使用する名前と電子メールアドレスを入力する必要があります。ただし、ここでの手順では、本情報を必要とするgit commitコマンドが登場しないため、この情報は使用されません。
最後に、GitリポジトリのURLに「https://github.com/kazuhitoyokoi/node-red-pose-estimation-demo.git」を貼り付けます。プロジェクト名は自動的に入力されます。また、その他のフィールドは空である必要があります。
この時点では、いくつかのNode-REDノードのモジュールが存在しないため、読み込み処理の後にエラー通知がポップアップ表示されます。この問題を解決するには、まず「プロジェクトの依存関係を管理」のボタンをクリックしてプロジェクト設定UIを開きます(もし、このボタンがない場合は、情報タブにあるプロジェクト名の横の「・・・」ボタンをクリックします)。
プロジェクト設定UIの依存関係タブには、次のようにいくつかのNode-REDモジュールが不足していることが示されます。これらのモジュールをNode-REDにインストールするには、各インストールボタンをクリックします。
モジュールが存在しない問題を修正すると、Node-REDのフローが、赤い点線を持つ不明なノードがないフローになります。
(3) エンドポイントを編集
エンドポイントのURLを、OpenShift環境にデプロイしたTensorFlowコンテナに変更するには、まずhuman-pose-estimatorノードをダブルクリックします。
エンドポイントの設定UIに移動するには、ノードプロパティUIにある鉛筆ボタンをクリックします。
エンドポイント設定UIのホスト欄については、前もってデプロイしておいたmax-human-pose-estimatorインスタンスからコピーしたエンドポイントのURLを貼り付けます。なお、名前のフィールドをopenshiftに変更することは必須ではありません。エンドポイント設定UIの「更新」ボタン、ノードプロパティUIの「完了」ボタンをクリックし、フローエディタの画面に戻った後、右上のデプロイボタンをクリックします。
(4) フローの動作確認
左上にあるinjectノードの左側にあるボタンをクリックすると、右上のimage previewノードの下に注釈付きの画像が表示されます。同時に、OKの画像が右下のimage previewノードの下に出現します。両者の間にあるanalyzeノードによって、人間が自転車に乗っているときの通常のポジションかどうかが判断されます。
次の手順として、2番目のinjectノードのボタンをクリックすると、結果として警告の画像が表示されます。右側の腕が上がったため、サイクリストがすぐに右に曲がるだろうと、analyzeノードを使用して判断されました。
必要に応じて、http requestノードだけではなく、その他のプロトコルを介して、その自動車に装備されているカメラに接続することもできます。
この記事では、Node-REDとRedHat OpenShiftにデプロイした他のコンポーネントとを統合する例を説明しました。MySQLノードのようなNode-REDノードモジュールで接続できる便利なコンポーネントがOpenShiftにはいくつかあるため、今後もこれらのコンポーネントを調べ、その他の利用例についても考えてゆこうと思います。
- 投稿日:2020-12-01T15:36:10+09:00
【保存版】個人開発の進め方 -全5ステップ-
はじめに
個人開発の手順をまとめました。
手順通り進めれば、必ずサービスは出来ます。各ステップが、なるべく省エネになるように意識して書いています。
本手順の目指すスタイルは、小さく繰り返すプラン②です
是非、参考にしてみて下さい。
( twitterやってます!@gjfcgmi )目次
STEP タイトル 内容 ① アイディアを探す まずはアイディアはどう探すのか?
実際アイディア出しに使ったエクセルを紹介② サービスを練り上げる 見切り開発を減らして、クローズ率を少しでも下げる ③ 時間を節約できる箇所? 時短ポイントを3つ紹介
④ 開発の進め方 開発着手前が大事なことを伝えたい ⑤ ユーザの獲得方法 RTされるコツとDM作戦が大事なことを伝えたい コンテンツ
① アイディアを探す
一番最初に、サービスの核となるアイディアを探します。
アイディアがなく、何から着手すれば良いか分からない時はよくあります。
3つ探し方を紹介します。①-1 美味しんぼ法
1. 他人のサービスを見る 2. いいなと思うサービスを探す 3. そのサービスを素因数分解する(どの要素がいいのか書き出す) 4. その類似サービスを探す 5. また素因数分解をする 6. 4.-5.を繰り返す。 7. 良い箇所を掛け合わせる 8. 要素を1つ変えて、サービスが成り立つか考えるイメージは、「美味しんぼ」です。
「美味しんぼ」の主人公が料理を食べた時に、具材を細かく言い当て・分析します。
そのようなイメージです?美味しんぼ法を実践した例
「インフルエンサー×広告主のC2Cサービス」を作ったときの結果です。
(一部消えていたので、参考程度ですが)①-2 他人のアイディアを見る
他人のサービスが見られるサイトを紹介します。
サイト名 紹介 Product Hunt
The best new products in tech.毎日かなりのサービスが投稿されています。
個人開発者から大手サービスまで、おそらく世界最大の投稿サービス
スマホアプリもあるので、暇な時に見るのがおすすめBetaList:
Discover and get early access to tomorrow's startupsUIが綺麗
個人開発は少なく、スタートアップが多いイメージStartup Lister 投稿数が少ないが、その分シンプルで見やすい。
まずはこのサイトを見てみるのがおすすめService Safari こちらも投稿数が少ないが、日本語サイトなので、ピックアップ。 慣れてきたら、機能がしっかりしている
Product Hunt
のみで良いと思います。①-3 友達の話は、しっかり聞く
他業種の友人が入れば、業務について根掘り葉掘り聞くようにしています。
その結果、いくつかサービスに結びつきました。
テストユーザになってくれる点も有り難いです。? Point (専門知識・業務フロー)x(IT技術)の観点を意識して話を聞くと、アイディアが浮かび易い ex: 弁護士 x 機械学習 → 類似判例検索エンジン↓のサービスは弁護士の知り合いとの話がきっかけで出来たサービスです。
新卒, Webサービスを作ってみた話他にも保育士や土地管理業者など様々な人の手を借りつつ、サービスは作っています。
② サービスを練り上げる
アイディア出しをしているうちに、いくつかサービスのイメージは出来ていると思います。
その中で、可能性の高いサービスを見つけて、さらに磨きをかけるステップです。②-1 コンセプト良し悪し占い
自身のアイディアに当てはめて、考えてみて下さい。
②-2 サービスのコンセプトを話してみる
誰かにコンセプトを話してみることをおすすめします。
きちんと言語化できるかは、早い段階で確認した方が良いです? Tips 開発終盤で、コンセプトが弱いことに気づくことは多々あります この段階で、類似サービスとしっかり違いを出せるか検討する時間を取った方が良いですこの段階で没案になる可能性は高いです。。
ダメでしたら、またアイディア探しに戻ります②-3 もっとサービスを練り上げる
収益を考えるなら、もう少しサービスを練り上げます
下記の項目をさらっと紙に書き出せるか試してみることをお勧めします。
項目 回答 サービスの全体像 解決出来ること ビジネスモデル ビジネスモデル(お金の流れ中心) 利用方法 市場の規模 収益構造 書き出している時に疑問点・類似サービスも見つかるので
STEP②には時間を書けるようにしていますビジネスコンテストではないので、ざっくりやってみるのお勧めです。
? 実例
C2Cサービスを作成した時のメモです。
(縦長になってしまうので、横で)
サービスの全体像 解決出来ること ビジネスモデル ビジネスモデル(お金の流れ中心) 利用方法 市場の規模 収益構造 ③ 時間を節約できる箇所?
③-1 デザイン
デザインは、費用対効果が高いので買う。
綺麗なコードが買えますし、何より安いです。
→「design template premium
」でぐぐるべしテンプレートが買えるサイトを一つピックアップします
WordPress Themes & Website Templates from ThemeForest自分は、こちらのサイトで買いました。
1. creative-tim
2. materializecss③-2 機能の精査
個人開発で即クローズは多々あります。
使われない機能に費やす時間は勿体無いので、実装する機能を一回精査します。
No. 問いかけ 問の意図 1 運用でカバー出来ないか サービス立ち上げ初期はそこまで人は来ません。
最初はお問い合わせベースで、件数増えてきたら機能実装でも良いはず2. 今の段階で、本当に必要か No.1と似ています。
例えば、決済画面。
会員数0で決済画面を作る必要があるのか。
段階リリースをして、手応えを感じてからでも良いはず3 ネイティブアプリである必要があるか リリース(審査)で時間がかかってしまうので、
特にネイティブである必要がなければ、webサイトで良いはず
手応え感じたら、側ネイティブを作って行く選択肢もあり4 代用出来るSaaSがないのか フルスクラッチで作成すると時間がかかるので、
出来る限り利用できそうなSaaSを見つけて、フリープランで試せないか探します
毎回使うのはこの辺り
VPSの代りにfirebase, 決済サービスStripe。firebaseはCronが設定出来るので、NoSqlで表現しづらい機能が実装できる。③-3 使うツール・サービスを絞る
個人開発では、企画・開発・マーケティングまで幅広く担当します。
学習コストもかなり膨らんでくるので、極力ツールを減らし、時短を狙うツール
ツール名 ざっくり説明 参考リンク エクセル 絶対絶対必須。
基本全部これで完結(Macであれば、Numbers)
調査からWBS,不具合管表までシートで分て1ファイルにまとめてます
↓こんな感じ- Sketch 図作成ツール。
エクセルで出来ないことは、スケッチでやっています。
記事の中の資料もスケッチで作っています学習用記事の該当箇所 IntelliJ IDEA
WebStorm超高機能なIDE
大抵の機能がデフォルトで備わっているのが、Goodですhttps://www.jetbrains.com/ja-jp/ 他のツールは使っていません。
サービス
サービス名 ざっくり説明 参考リンク Googleアナリティクス アクセス集計 アクセス解析担当が必ずやってるGoogleAnalytics設定のまとめ【2018年版】 Firebase
Herokuサーバの代わり。
ドメイン設定も簡単に出来る。SSL設定も。公式サイトががわかりやすい。
Firebase・HerokuFreeLogoDesign ロゴ自動作成ツール 他にも色々あるみたいなので。
https://liskul.com/logomaker-29137git等は割愛です
④ 開発の進め方
④-1 開発着手前
1. 思いつく機能を書き出す。 2. 必須な機能を絞る(参考: ③-2 機能の精査) 3. 機能の優先順位を決定 4. 開発順番を考える 5. スケジュールに落とし込む 1. 開発順番が早い順に機能を書き出す 2. 実装をイメージして、一段階深掘り 3. ざっくり日数見積もり 4. 見積もり結果を踏まえて、どこで初回リリースするか決める? 「5. スケジュールに落とし込む」の実例
C2Cサービスを作成した時の予定表を使います
5.1
~5.3
で出来上がる成果物を添付しました。
ステップ 5-1.
開発順番が早い順に機能を書き出す5-2.
実装をイメージして、一段階深掘り5-3.
ざっくり日数見積もり成果物 青セルは実施予定日 ④-2 開発中
予定表通り進めて、必要に応じてスケジュール変更を繰り返すのみ
開発中に出てきそうな疑問をQ&A形式で紹介します。? Tips ウェブサービスを作るなら、スマホに最適化を。 立ち上げ初期は、9割スマホからのアクセス。その後、しばらく7割程度に落ち着く (あくまでも体感)⑤ ユーザの獲得方法
SEO対策
一応導入するが、今はそこまで時間はかけない。(追々対応する。
下記のサイトを参考に、キーワードをよしなに作る。
- Google トレンド
- ラッコワードSNS投稿作戦
RT伸びやすいようにコツを紹介
1. サービス概要が分かる画像を添付 2. 読み飛ばされないように、改行は沢山使う 3. シンプルに 4. 「-----」、絵文字等でもっとシンプルに 5. 最後に何をして欲しいかを一言でDM作戦
最低限の機能の実装が終わったら、一回開発をストップ & 検証フェーズへ
DMを送って、受信者の反応を見て
クローズするか、もう少し開発継続するか決めています。意外とやっていない人多いので、DM作戦お勧めです。
返信率は、ざっくり50%~70%くらいです。
(スパムのように送ってしまうとまずいので、その辺りは考慮が必要です)終わりに
まだまだ書きたい事があります。。
後半、疲れが出てしまいました。
特に、集客パート。
開発続けるか、クローズするかのKPI表についても書きたかった今後アップデートしてきます
変更通知送るので、ストック?いいねして頂ければと思います。個人開発やっていて思うことは、楽しいです!
以上!!
- 投稿日:2020-12-01T15:09:07+09:00
【入門者向け】Canvas入門講座#14 2値化してみよう【JavaScript】
問題14
問題13~15は問題9がベースとなっています。まず問題9を実装してください。
画像読み込み後、binarize押下時に2値化処理をせよ。
2値化処理はグレースケール後、127より大きい場合は255,それ以外の場合は0となるように処理すること。グレースケールとは以下の式で表せる。
Gray = 0.299 * Red + 0.587 * Green + 0.114 * Blue;以下のHTMLを使用すること。
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <title>問題14</title> <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script> <script> $(() => { $('#my-file').on('change', e => { const img = new Image(); $(img).on('load', e => { }); img.src = URL.createObjectURL(e.target.files[0]); }); $('#binarize-button').click(e => { }); }); </script> </head> <body> <canvas id="my-canvas" width="500" height="300"></canvas> <br> <input id="my-file" type="file" /> <br> <input id="binarize-button" type="button" value="binarize" /> </body> </html>解答
問題9の分の回答も併せて貼っておきます。
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <title>問題14</title> <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script> <script> $(() => { $('#my-file').on('change', e => { const img = new Image(); $(img).on('load', e => { // コンテキストを取得 const ctx = $('#my-canvas')[0].getContext('2d'); // canvasを黒色で塗りつぶす ctx.fillStyle = '#000000'; ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height); // canvasと画像のアスペクト比を求める const canvasWidth = $('#my-canvas').prop('width'), canvasHeight = $('#my-canvas').prop('height'); canvasAspect = canvasWidth / canvasHeight; // canvasのアスペクト比 imgAspect = img.width / img.height; // imgのアスペクト比 // canvasと画像のアスペクト比を比較し、貼り付ける領域を決定する let dstWidth, dstHeight; if(canvasAspect > imgAspect) {// canvasの方が横長 dstHeight = canvasHeight; dstWidth = dstHeight * imgAspect; } else {// canvasの方が縦長 dstWidth = canvasWidth; dstHeight = dstWidth / imgAspect; } // 画像を貼り付ける ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, dstWidth, dstHeight); }); img.src = URL.createObjectURL(e.target.files[0]); }); $('#binarize-button').click(e => { const canvas = $('#my-canvas')[0], ctx = canvas.getContext('2d'); ctx.save(); const imgData = ctx.getImageData(0, 0, canvas.width, canvas.height), data = imgData.data; for(let p = 0; p < data.length; p += 4) { // rgbを求める const red = data[p], green = data[p + 1], blue = data[p + 2]; // グレースケールの値を求める let gray = 0.299 * red + 0.587 * green + 0.114 * blue; // 2値化する if(gray > 127) { gray = 255; } else { gray = 0; } // グレースケール化する(グレーで塗りつぶす) for (let i = 0; i < 3; i += 1) { data[p + i] = gray; } } // imageDataをcanvasに貼り付ける ctx.putImageData(imgData, 0, 0); ctx.restore(); }); }); </script> </head> <body> <canvas id="my-canvas" width="500" height="300"></canvas> <br> <input id="my-file" type="file" /> <br> <input id="binarize-button" type="button" value="binarize" /> </body> </html>解説
問題13と酷似しているのでポイントだけ説明します。
グレースケール化するときの値を計算して、閾値(今回は127)より大きい場合は255,それ以外は0にします。// グレースケールの値を求める let gray = 0.299 * red + 0.587 * green + 0.114 * blue; // 2値化する if(gray > 127) { gray = 255; } else { gray = 0; }発展
2値化の閾値は手動で決める場合と、計算して求める場合があります。
計算で2値化の閾値を求める方法として「大津の2値化」というロジックが有名です。
興味のある方は調べてみてください。
- 投稿日:2020-12-01T15:07:20+09:00
[react-native]FlatListの使い方
目的
App.jsにを表示する。レンダーする要素は別コンポーネント化。
App.js
import React, { useState } from 'react'; import { StyleSheet, View, FlatList } from 'react-native'; import GoalItem from './components/GoalItem'; export default function App() { const [courseGoals, setCourseGoals] = useState([]); //courseGoalsには以下のような配列が入っている。 Array [ Object { "id": "0.1283113872926156", "value": "Aaa", }, Object { "id": "0.1515628656002196", "value": "Aaa", }, ] // return ( <View style={styles.screen} > <FlatList keyExtractor={(item, index) => item.id } //デフォルト値なので省略可 data={courseGoals} // ここに繰り返したい配列をいれる。 renderItem={itemData => <GoalItem id={itemData.item.id} title={itemData.item.value} />} // viewの部分 引数itemDataの.itemプロパティに配列の要素が一つづつ繰り返し入ってくるような感じ /> </View> ); }GoalItem.js
レイアウトは任意
import React from 'react'; import { View, Text, StyleSheet,TouchableOpacity } from 'react-native'; const GoalItem = props => { return ( <TouchableOpacity> <View> <Text>{props.title}</Text> </View> </TouchableOpacity> ); }; export default GoalItem;所感
意外と癖が強い。
繰り返しの部分はそれぞれにユニークなkeyを指定しないとreact nativeが警告を出す。
特定のリストを消す時にfilterメソッドを使ったりする時にも使える。