なめこ備忘録

プログラミングに関する備忘録や経験したこと,考えたことなど好き勝手書きます.

[tex: ]

2022年を振り返って

死ぬほど久しぶりななめこです. 1年に数回ほど書こうかと思い立つ瞬間もあったのですが,大体書き始めてしばらくすると「まあいっか」ってなるのを繰り返した結果500年ぶりくらいの投稿です.twitterの出現頻度も低くなったりはしていますが生きてます.それなりに元気です.

ちょっと今年はほんっっっっっっとうに濃かった一年で,これを記さずして何を記すという感じなので久しぶりに筆を取りました.日記と思想と時々戒めみたいな内容になると思いますが数年ぶりの駄文にお付き合いください.

何があったの?

一言で言うと死ぬほどお仕事が忙しかったです. 突然降って湧いたような忙しさではありましたが,その分得られるものも多かったので文句を言いたいわけではないです.あ,いえ文句がないと言ったら嘘になると言うかそれなりにブチギレ案件はあったと言うか...とりあえず今は横に置いておいてヨシッ!

実を言うと手を抜いてこなすという選択肢もあったのですが,ちょうどそのタイミングで久しぶりに会った(たぶん入社以来)同僚から「お前がその給料はおかしい」と言われて待遇に異を唱える際の交渉材料にもなりそうだったのでめちゃくちゃ大真面目に取り組みました.意識を大きく切り替えるきっかけにもなったので本当にこの同僚には感謝しています.珍しく.

結果として,忙しさと面倒なあれこれを生贄により大きな裁量と待遇の大幅な改善を勝ち取ることができました.待遇とオブラートに包んでいますが要するに給料です.お金です.オカネ,ダイジ.
大きな裁量を持つことがメリットかデメリットかは人によると思いますが私はメリットと捉えるきのこです.なんかあれこれ口出ししたくなることない?私はある.今はめちゃくちゃ言いたいこと言う人になってます.

お仕事

今年のGW明けくらいからなんかでかい規模のプロジェクト立ち上げの中核メンバーに突然据えられました.ドウシテ??
もう少し具体的にはWEB系のプロジェクトです.ちなみにこれはこの話に全くさっぱりこれっぽっちも関係ないんですが私は機械学習を中心とした研究開発のエンジニアです.ナンデ????
とまあ大量の「???」を抱えたまま突然キックオフしたプロジェクトでしたが,コードを書く仕事というよりはかなり上流工程の内容で,学生時代レベルのWEB知識を引っ張ってきて色々考えてやってみたら案外9割型正解は引けてたっぽかったので,途中からは諸々開き直って突撃してました.

紆余曲折はありつつも目の前に無限に降ってくるタスクを片っぱしから潰していったらなんか想定以上の働きだったようで大きな評価が返ってきました.当時の気分は「あれ?私また何かやっちゃいました?」です.どうやら転生者だった模様.

正直仕事内容的には本来のやりたいことからは結構ずれているので普通にしんどさはあったのですが,まあそれはそれとして待遇改善という意味では価値があったと思ってます.

その後落ち着く間も無く別のこれまた大きいプロジェクトの今度はリーダー的なポジションに据えられて色んなものと戦う毎日でした.戦闘民族です(ただし物理はゴミでメンタルはお豆腐です).
こっちは基本めちゃくちゃ楽しかったです.ウッキウキで仕事してました.技術が関係ないお仕事?他の人にお任せできない?だめ??
たーのしー!ってお仕事しつつも自分の未熟さにも直面したいい機会でした.マネジメントが死ぬほど下手.タスクの抱え癖直してちゃんと他の人に振ってください.

とまあそんなこんなしているうちに気づけばここまできてました.今年は時の流れが早いですね.

せっかくの機会なので去年までと今での仕事への取り組み方の違いも書いておきます.

去年までの仕事ぶり

通称「腐ってた時期」です.この時期は「給料レベルの仕事しかしない」ことを意識してました.

包み隠さず表現すると,給料に見合った(と自分が思っている)レベルの仕事量だけをこなし,残りは適度にサボってました.でも給料分の仕事はしてたので全然悪いことはしてないつもりです. あとこれは性格的な部分も関係してますが,量は絞っても質に関する部分は手を抜かないようにはしていました.あれ,もしかしてそれだと普通に給料+αみたいな仕事してますか?算数って難しいですね.

入社当初は一瞬ちょっと査定が+大きめになるかなという仕事をしたこともあったのですが環境や状況のミスマッチもあってか上手くいかず,一旦諦めて大きく引き上げるチャンスを伺う方にシフトしていました. まあレベルが10上がるはずの強い敵を倒したのに1しか上がらないのであれば最初から1上がる最低ラインの敵を倒した方がコスパいいですからね.
今となっては若干悪手寄りだったとは思いつつも当時の盤面の見え方とか手札状況的には3番目くらいの手ではあったかなとは思ってます.その時蒔いた種が芽吹いた部分もあるので後悔しているというわけではないです.まあ人生いろいろなんで停滞する時期もあります.

今の仕事ぶり

通称「覚醒後」です.ガチャで二枚目引いたんですかね.完凸だと後の伸び代が残ってないことになるので完凸じゃないといいなぁ...
覚醒とは同僚の言ですが,個人的な感覚としては新しく目覚めたと言うよりは過去の自分が帰ってきた感じがしています.お帰り,私.人はね,一度鬱になるとなかなか戻ってこれないんだよ.

今は「自分がやりたい・やるべきだと思ったことをやるためにできるだけのことをする」ことを意識しています.当たり前ですが自分の意思は自分で発信しないと伝わりようがないのでできるだけ全部言うようにしています.その結果「自分がやりたいことをできるようにするための手っ取り早い手段」として権限と裁量がどんどん高くなってます.こんなつもりでは...
言いたいことは言うしやりたいことはやるしを貫いてありとあらゆるものに関わって抱えていった結果,気づけば立派なワーカーホリックと化している自覚があります.まあ元々社畜適正高かった気がするので時間の問題でしたね.諦めましょう.

一つ不満があるとすれば裁量や権限を得た副作用として技術のみに注力はできなくなったというところですかね.とは言え以前までの状況だと注力したい技術が選択肢にならないという致命的なバグがあったので仕方のない部分ではあります.一度土壌作って安定稼働始めたら退職してただの一エンジニアとして再就職したい...

生活

仕事の変化は生活にも現れています. まず食生活が変わりました.自炊する余裕もなかったので出前とかで食事を済ませる頻度がめちゃくちゃ増えました.東京が便利すぎるのが悪い.
人との交流が増えて外食頻度も増えました.美味いものは美味いんだと知った.
交流ついでにお酒を飲む機会も増えました.飲まなきゃやってらんねぇですよ.でも二日酔いは死ぬほど苦しいのでみんな気をつけようね!

あとお給料増えたのでお引越ししました.わんえるでぃーけーってやつです.広いっていいね...
置き場が出来たので食洗機も導入しました.神.次はドラム式洗濯機が欲しいです.人生が変わるらしいので.

総括

今年を一言で表すのであれば「波乱」とかかなというお気持ちでいます.次いで「栄転」ですかね.とんでもない忙しさの中にいつつも所謂ライフステージが2・3段階飛んで進んだ感があるので,波乱:栄転=7:3くらいな気がします.

精神状態にも余裕が出てきて昔の感覚が戻ってきたのは大きな収穫です(というか今までそれに気づけていなかったことに驚きました).ただ困ったことに体力面とかは明らかに落ちてるんですよね.ちょっとの無理がすぐに体にくる...もしかして今が筋トレの機運ですか?

生活面は破綻する瞬間が出てきてるのでもう少し気をつけたいです.びょうきにかかるととってもたいへんでたいへんでした.

今年はいろんなことが大きく動いて激動の一年だったので2年くらいゆったりとしたいです.
ということで来年はきっとゆったりとした1年になるよね,ハ●太郎?
???「いや...ならんが...」

Ubuntu 18.04 LTS + RTX2070でDeep Learning環境を整えるまでの備忘録

既に何度も環境構築をしたことがあるけど,毎回調べていると情報が錯綜しすぎてて苦労するので備忘録として残しておく.

OSの種類とかバージョンとか載ってるGPUだとかで,バージョンだけでなく使える方法も変わってくるので注意が必要.

事前準備

nvidia driverのバージョン確認

以下URLからnvidia driverの対応バージョンを調べておく.

https://www.nvidia.co.jp/Download/index.aspx?lang=jp

tensorflowの対応バージョン確認

tensorflowは結構厳密にバージョン指定をしているので以下URLで事前に確認しておく. 今回はtensorflow 1.13を入れる.

https://www.tensorflow.org/install/source

python

とりあえず3系をインストールしておく.

tensorflowのバージョンによって対応しているpythonバージョンも異なるので注意.

(nvidia driverのインストール)

もしかすると必要のないかもしれない手順(次のcudaインストールでドライバーごとインストールされているっぽい?).

nvidia driverの入っているリポジトリを追加し,aptからnvidia driverをインストールする.
今回はUbuntu 18.04 TLS & RTX2070に対応しているバージョン430をインストール.

$ sudo add-apt-repository ppa:graphics-drivers/ppa
$ sudo apt update
$ sudo apt  install nvidia-driver-430
$ nvidia-smi

で確認

cuda10.0のインストール

ネットワーク経由のインストーラータイプでCUDA 10をインストール.

$ sudo apt install cuda

でもインストールは可能だが,tensorflowはCUDA 10.0に対応しており,デフォルトだと最新版である10.1(10.2?)がインストールされるためバージョンを指定してインストール.

$ sudo apt-key adv --fetch-keys http://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64/7fa2af80.pub
$ wget http://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64/cuda-repo-ubuntu1804_10.1.168-1_amd64.deb
$ sudo dpkg -i cuda-repo-ubuntu1804_10.1.168-1_amd64.deb
$ sudo apt update
$ sudo apt install cuda-10-0
$ reboot

$ export PATH="/usr/local/cuda/bin:$PATH"
$ export LD_LIBRARY_PATH="/usr/local/cuda/lib64:$LD_LIBRARY_PATH"
$ nvcc -V

で確認.

この時,nvidia-smiとnvccで表示されるCUDAバージョンが異なる場合があるが,気にしないで大丈夫そう.

cuDNN7.4のインストール

以下URLよりcuDNNをダウンロード(登録が必要).

https://developer.nvidia.com/rdp/cudnn-download

  • cuDNN Runtime Library for Ubuntu18.04 (Deb)
  • cuDNN Developer Library for Ubuntu18.04 (Deb)
  • cuDNN Code Samples and User Guide for Ubuntu18.04 (Deb)

この3つをダウンロードする.

以下コマンドでcuDNNをインストール

$ sudo dpkg -i libcudnn7_7.4.2.24-1+cuda10.0_amd64.deb
$ sudo dpkg -i libcudnn7-dev_7.4.2.24-1+cuda10.0_amd64.deb
$ sudo dpkg -i libcudnn7-doc_7.4.2.24-1+cuda10.0_amd64.deb

以下のようにコマンドだけでもインストールは可能とのこと(試していない). 以下のコマンドだとcuDNN 7.5が入る.

$ echo "deb https://developer.download.nvidia.com/compute/machine-learning/repos/ubuntu1804/x86_64 /" | sudo tee /etc/apt/sources.list.d/nvidia-ml.list
$ sudo apt update
$ sudo apt install libcudnn7-dev=7.5.0.56-1+cuda10.0

各種フレームワークのインストール

tensorflow 1.13

pip installで必要なものは全部入る.
基本的にデフォルトだと最新のものが入るので必要ならバージョン指定する.

$ pip install numpy scipy scikit-learn pillow h5py
$ pip install tensorflow-gpu

keras

tensorflowが入っている環境でpip installするだけ.

$ pip install keras

chainer

numpy, cupyも含めてまとめてpip installするだけ.

$ pip install numpy cupy chainer

参考

UbuntuにNvidia GPUのDriver + CUDAをInstallする(GTX1080対応版) - Qiita

Ubuntu 18.04へのCUDAインストール方法 - Qiita

初めて自作PCを組んでみた

こんにちは,なめこです.

今回初めてPCを自作したのでその備忘録及び日記的な何かを残します.

目的

機械学習,というよりDeep Learningを気軽に試せるローカル環境が欲しくなったのでデスクトップPCが必要になりました. 流行りのクラウド環境にも一度は手を出したのですが,実行時間が金額に直結すると考えると「とりあえず学習させる」ということに中々踏み出せず断念しました...

BTOモデルではなく自作を選択した理由としては,

  • 単純に数万単位で安くなりそう
  • 欲しいところだけスペックを上げるみたいなことがしやすい

あたりですかね.
個人のDeep環境用のBTOモデル自体少なく,今回の自分にとっていい感じのものがなかったというのが大きいです.

完全初心者だったのでマザボ選びとかその他諸々のパーツのメーカーや型番をどう選べばいいのかわからなくてずっとぐるぐるしてたのですが,それはまた別の話.

予算

まずBTOモデルで要求をある程度満たしているものを調べたところ20万くらいでした. その上で自作PCの記事とかをいろいろ確認して,15万くらいあればなんとかなるかなって感じだったので予算は15万前後としました.

パーツ紹介

ほぼ全てのパーツに関しての知識がなさ過ぎてどういった基準で選べばいいのかわからなかったので,調べたり有識者に聞いてみたり1人で勝手に悩んだりしながら最終的にえいやって感じで決めていきました.

たぶんもう少しいろいろ吟味すればもうちょっと安くなったと思います.後悔はしてないのでいいですが.

CPU

インテル Core i5-9600K

最近ようやくインテル製のCPUの性能感がわかるようになってきたのでインテルにしました.
AMDはまだよくわかりません.

Deep環境ならCPUよりGPUとかメモリにお金かけた方が良いとのことだったのでi5です.

ゲームをする予定がないですし(やったとしてもそんなに重いのしない気がする),OCさせるつもりがなかったのでi5-9400とかを買う予定だったのですが,購入タイミングで色々あって気付いたら9600Kになってました.
てことでOCは飾りです.Kがついてるだけで強そうなのでまあいいでしょう(?).

マザーボード

ASRock Intel Z390 Extreme4

9世代CPUに対応,メモリ最大64GBまで載せれる,GPUが3つ載る,SLI対応(Zシリーズ)の要件を満たすものを選択.

拡張性をメインで考えました.
ただメモリはともかくとして,GPUを3つも載せることになるのかは疑問が残りますね.いっそマイニングでもさせますか.

ある程度欲しいスペックを定めた後は価格と雰囲気で選びました.

PC周りってなんでやたらと光るんですかね.

GPU

GIGABYTE グラフィックボード NVIDIA Geforce RTX 2070

Deep Learningの核となるGPU,結構迷いに迷いましたが,性能の高さとコスパを考えてRTX2070を選択.

メーカーを何基準で選ぶべきなのかの知識も経験も皆無だったので価格と見た目の感じで選びました.(玄人志向なめこが玄人じゃないのでやめました(?))
本音を言うと2080とか2080Tiとか欲しかったのですが,価格的に手が出ないなって感じだったので断念.(メモリ的にGTX1080Tiも一瞬視野に入ったけど高騰し過ぎてやばかったのでそっ閉じ)
将来的にはTitanとかTeslaあたりをホイホイ買えるようなお金持ちになりたいですね.

RTX2070だとSLIできないのでどうなんだろうとは思ったのですが,別にDeepでマルチGPU使うときはSLIを使わないとダメというわけではないですし,そもそもマルチGPUする人になるかすらわからないので割り切りました.
そう考えるとマザボも考え直してもいいのではって感じでしたが考え直すのが億劫だったのもあってえいやってしました.

因みにSLIという点ではGTX1070, 1070Ti, 1080あたりもできますが,単純にしばらく1GPUでやっていくことを考えるとまあ性能とコスパ的に2070がいいかなぁって感じました.(正直そんなに詳しくないので雰囲気.雰囲気でGPUを選んだ)

メモリ

CORSAIR DDR4 VENGEANCE LPX Series 16GB×2枚キット

画像を扱うならあればあるほど嬉しいメモリ.2万あれば32GB買えるとのことだったのでとりあえず32GB分を確保.
将来的には64GBまで増やしたいです.なくてストレスになるくらいならいっそ使い切れないほど欲しい派.

DDR4なら特に問題はないでしょうといった感じで選んでました.

余談ですが,今の所自分が新しくPC(ノート含む)を買うごとにメモリが倍になっているので2世代後くらいには128GBになってるかもしれません(なってない).

SSD

Samsung SSD 250GB 970 EVO Plus M.2

最初はSATAタイプを選んでたのですが,マザボがM.2対応なのにもったいないとお叱りを受けたのでこちらに変更.

とっても速いらしいです. 自分の持ってるPCがMacbook Air以外だと6年ほど前のSSDですらないdynabookなのでたぶん普通のSSDでも爆速気分だったと思います.

HDD

Seagate BarraCuda 3.5" 2TB HDD

6千円程度で2TBのHDDが買えるんですね.思ったより安くてびっくりでした.

特に何も考えず容量と価格で選択.

ケース

Thermaltake Versa H26 Black

ミドルタワーの安いやつ.それ以上でもそれ以下でもない脳死選択です.

強いて言うなら片面アクリルで中見えるとか,なんか標準搭載のファンが光るらしいとかはプラス査定でしたね.

電源

玄人志向 NEXTシリーズ 80 PLUS Bronze 600W ATX電源

ひとまず600Wあれば足りそうだった(そんな気がした.大丈夫だと思う.たぶん)のでお安いのを雑に選びました.

DVDドライブ

ASUS DRW-24D5MT

こちらもとりあえず安いものを選びました.

最悪必要ないものだった気がしないでもないですが,大した額ではないですし,必要になった時に困るよりはいいでしょう.

CPUクーラー

Thermaltake Contac Silent 12 サイドフロー型CPUクーラー

はい,購入直前でCPUをOCモデルにしたため完全に忘れていたCPUクーラーくんです.
これがないせいで他のパーツが届いてすぐに組むことができず,秋葉原まで買いに行きました.

せっかくなので(?)サイドフロー型を購入.取り付けが上手くできるわけがないのでプッシュピン方式です.

価格

価格の概算を置いておきます.

CPUクーラーを除き全部Amazonで一括で買ったのですが,最終的に156,000円程度になりました.
BTOよりは圧倒的に安いし,普通のショップでパーツ買い揃えるよりも安いし,なんなら全部お家まで持ってきてくれるので楽で最高にお得な気がします.
不安からちょっと高めのにしたりってのも含めてこのお値段なので満足です.

パーツ 値段
CPU 31,000
マザーボード 20,000
GPU 58,000
メモリ 18,000
SSD 9,000
HDD 6,000
ケース 4,000
電源 5,000
DVDドライブ 2,000
CPUクーラー 3,000
合計 156,000

組み立て

ドライバー片手に頑張りました.

組み立てること自体は初めてではなかったのですが(高専で一度組み立てからサーバ構築までやったはず),何年も前のことは覚えてなかったので実質初めてでした.
わからない部分(全部)は各種マニュアルとブラウザを行き来しながらなんとかしました.

CPUクーラーのプッシュピンの入りが甘くてクーラー設置時にプッシュピンが空を飛んだり,HDDを中々認識してくれなかったり,BIOSの設定難しいなぁってなったりしてました.
配線の綺麗さ?知らない子ですねぇ...

f:id:NAMEKO:20190615040145j:plain

こんな感じで青く光ります.特に考えずにパーツを選びましたがいい感じに寒色系で統一されました(?).

起動まで

Deep環境用ということでOSはUbuntu 18.04を選択.
手元にあったusbメモリをインストールディスクとして利用しました.

以下mac側のterminalのプチ備忘録

$ diskutil list
$ diskutil unMountDisk /dev/diskN
$ sudo dd if=(ubuntu image file) of=/dev/rdiskN bs=1m

あとがき

時間はかかりましたが,なんとか構成を考えて購入して組み立てて起動するまで走り抜けることができました.

CPUとかの最小構成パーツだけをマザーボードに取り付けた状態でBIOS起動した時が一番テンションが上がっていた気がします.
最後の方は疲労で死にかけていたので腰痛くらいしか覚えてないです.

さて,なんとかマシンが動くところまではきましたが,次はGPUを扱うための各種セットアップが待っています.
こちらは何度か経験はしていますが未だ慣れないので引き続き頑張っていきたいです.

つくばに来て1年が経ったので色々まとめてみた

こんにちは,なめこです.

つくばでも桜を見かけるようになり,気づけばすっかり春だなぁって感じの今日この頃.

そういえばもうつくばに来てから1年過ぎたんだなって若干感慨深くなったので,つくばのこととか,つくばに来てからのこととかを徒然なるままにまとめてみることにしました.

多分例によって長文化しますので悪しからず.短くまとめられるようになりたい(願望).

つくばのこと

環境

東京の秋葉原からつくばエクスプレス(通称TX)に乗り,1時間(快速なら45分ほど!早い!)ほど揺られるとたどり着く未開の地,それがつくばです.
...なんて言うとつくばを愛する方々には怒られる気がしますが,あながち間違いではないと思います.
終点であるつくば駅から出ると一瞬そこそこ都会かなと感じますが,筑波大学方向に進んでいくと見事な森が待っています.

自分の出身地もそれなりに田舎だと思っていましたが,「本当に関東圏なのん?」と疑問に感じるほどつくばは田舎です.

まず街灯がとんでもなく少ないです.ないところは本当に何もなくて夜になると真っ暗です.ライトで照らさないと何も見えないレベルで真っ暗です.
ただその分(?)星が綺麗です.少なくとも実家よりは綺麗でした.

飲食店,スーパー,コンビニ以外のお店が極端に少ないように感じます. 生きる分には問題ないですが,色々買い揃えようと思った時にTXで移動するか車を使うかしないと厳しいです.
あと遊ぶ場所はほとんどないです.おうちに集まってSwitchでもしましょう.

これは田舎とか関係ないですが,地盤がゆっるゆるです.
どれくらいゆるゆるかというと,近くを大型のトラックが通ると建物が揺れるような場所があったり,木の根っこに負けて歩道がガタガタする程度にはゆるゆるです.
道路の凹凸がいっぱいあるので歩いているだけで足腰が鍛えられます(たぶん).

あと地震が多いです.頻繁に揺れます.
そして揺れると「地下で◯実験」とか言い始める人が一定数います.本当にやってても不思議じゃない気がするのが不思議.

でも足元ばかり気にしているとそれはそれで危険です.
いつ屋根が落ちてきてもおかしくないのですから...

移動手段

学生たちの主な移動手段は自転車です.南北に5kmにわたって伸びる大きな大きな大学内を,たくさんの自転車が走り回っています.
運がいいとセグウェイの類を見ることもできます.なんて言うか自由ですね.

つくば駅から大学をぐるっと一周する循環バスも走っています.1周30分かかるというので驚きです.
そこそこの頻度で走ってますが,十中八九遅れてくるので注意が必要です.
しかし遅延読みでのんびり行くと定刻通りの運行をしていて逃したりもします.バスは難しいです.
因みに1年定期が8600円で買えます.大体月2往復もすれば十分に元が取れるので買い得です.

東京,というか外界へ出るためのほぼ唯一の手段であるみんな大好きTXですが,1時間は思ったより長いです.
そして高いです.東京での移動も考えると往復3000円は覚悟しましょう.時間的にも金銭的にも心理的にも,東京は想像以上に遠いところにあります.
さすが陸の孤島つくば.

車を持っているととんでもないレベルで重宝されます.そしてコストコパーティーの買い出し要員として駆り出されます.

住む場所

まず大体の人は自分の学類を元に住む場所のあたりをつけます.南北に5kmもあるんですから当たり前ですね.

あとは買い出しのしやすさなどでも決めたりしますが,去年つくばの真ん中あたりに新しいスーパーが増えたので選択肢は増えそうです.
因みに完全キャッシュレスです.あとお酒とつまみのバリエーションが豊富です.

もしこれからつくばの家を探す学生さんがいらっしゃいましたら,循環バスの経路付近に住むことをお勧めします.
インターンだとか就活だとかで頻繁に東京に行くことになってもそこまで大変じゃないです.

友達を自分の家に呼んだり東京に頻繁に行く可能性がある人は間違っても桜とかには住まないようにしましょう.
「遠いから」って言われて誰も来ませんし,つくば駅に行くまで時間がいっぱいかかるのでその時点で外出が嫌になります.

家賃相場はとっても低いです. 1K・8畳・風呂トイレ別・広々キッチン・室内洗濯機置場な条件でお部屋を借りる場合, リッチな感じの場所でも4~5万,大学の真ん中付近は3~4万くらいあれば住めます.
東京の真ん中で同じような部屋に住みたくても10万以上は当然のようにかかるのですごいですね.

北は北海道,南は沖縄まで.あと日本に限らず海外の人も多く,まさに多種多様な人がいます.ぐろーばる.
色んな訛りが聞けるのも楽しいです.

サークルに入ると色んな専攻の人と交流ができます.医学の人,心理の人,数学の人,地球の人,情報の人...etc.
いろんな価値観を持っている人と関わるのはとても楽しいです.場合によっては疲れますが,それでも多くの人と関わっておくのは悪くないと思います.

基本的にはいい人がいっぱいいると思うので積極的に関わっていきたい所存.

つくばに来てからのこと

言いたいこと言いたくないこと,言えること言えないこと,色々ありますが一度振り返っておきたいのでダイジェストで1年をお送りします.

入学から夏休みまで

期待に胸を膨らませて,とはいかなかった気がします.色んなことがうまくできるかなとビクビクしながら入学しました.
入学前に3年次編入生の集まりが何度かあり,知り合いはそれなりにいたので比較的気が楽でしたがそれでもビクビクです.

入学式で「Imagine The Future」を歌っているのを見て「これが大学か...」と圧倒されました.

入学式後に歩いていると大学2年生の方に話しかけられました.どうやらサークルの勧誘のようでした.当たり前ですが向こうはこちらを1年生だと思って話しかけています.まあ新1年生に見えますよね...ごめんね,実は3年生なの.
ときどき1年生と偽って話を続ける悪い遊びをしていました.いや,というか切り出すタイミングがわからない...コミュ障.

アルバイトを始め,サークルに入り,授業をいっぱいいっぱい詰めて自炊もちゃんとして...といった生活をしていました.
今考えると「なんで全部できると思ったの?」と小一時間問い詰めたくなるレベルであれこれ詰め込んで頑張っていました.
たぶん親元離れて大学まで来たんだから多くのことを完璧にやらなきゃと思い込んでしまっていたんですね.無理は良くないです.

まあ無理がたたって早々に体と精神がやられました.この頃は本当に何も楽しめていなかったと思います.

アルバイトをやめると体と精神にちょっと余裕が戻りました.何事も頑張りすぎるのは良くないなって感じです.

アルバイトを辞めたためお金がなくなってきてやばいと思いインターン先を探し始めました.
序盤はいっぱい落ちてメンタルブレイクされていましたがそれはまた別の機会に(たぶんない).

夏休み明けから年末まで

休み明けのタイミングから週1,2ペースで長期のインターンを始めました.
ちょっと無理やりインターンに行く日を空けたので(家から職場まで片道2時間かかるので1日勤務にしないと行けない),まだちょっと授業は詰まってましたが,やりたいことができていたので忙しいながらも充実していました.

この頃にコンテスト参加もしていました.久しぶりのデスマでした.

後半まあ,いろいろあって精神がやられていました.
この時知ったんですが,ストレスで味覚がおかしくなったりするんですね.初めて知りました.知見を得た.
あとストレスで食べるようになる人と食べなくなる人がいますが自分は後者でした.食べなくなると栄養が不足して悪循環に陥るので無理にでも食べましょう.
因みに個人的にはココアがお勧めです.あったかいやつ.亜鉛が多く含まれていてストレスに効くみたいなので最近は頻繁に摂取しています.

年始から今まで

考えに考えた結果院進をやめることにしました.
詳細はこちら(超絶長いので注意).

nanameko.hatenablog.com

そこからは基本インターンと就活とサークルをぐるぐるしています.
就活の話はまた今度まとめます.

この前サークルの合宿に参加してきました.頑張っていろんな人と話しました(えらい.みんな褒めて).
あと色々あって寸劇をしました.どうやらなめこは演技派なようです.

1年を振り返って

いろんなことに挑戦して,いろんな人と出会った1年でした.
そもそも地元を出ての一人暮らしなので,いろんなことが初めましてでした.

正の面でも負の面でも多くのことを体験しました.
負の面はまだ消化しきれていないものもありますが,とりあえず自然に消えない限りは墓場まで持っていく所存です.

まわりはいい人が多いのですが,やっぱり既にできているコミュニティに飛び込むのは難しかったです.
サークルも最近ようやく溶け込めるようになってきた気がします(もう引退済みですが).

食事をちゃんととって精神的に安定してから(たぶん今も若干不安定)色々振り返ってみると,入学直後くらいからずっと心が不安定な状態だったんだなと感じました.
精神的不安定な状態は自分がそうとは気づかないというのが怖いところですね.定期的に誰かと話すとか美味しいもの食べるとかひたすら寝るとかして無理をしすぎないようにしたいです.

先にも言いましたが,いろんなことを上手くやろう,完璧にやろうと神経質になりすぎていました.
もう少し余裕を持って,雑にゆるく生きられるように頑張らないことを頑張りたいです.

以下自分への戒めも込めて.

  • お金の管理は多少雑なくらいがちょうどいいです.
  • ご飯は隔週でもいいので時々は何も考えずに好きなものを食べましょう.ちょっとした外食くらいしてもいいじゃない.
  • 間食を増やして甘いものをいっぱい摂取しましょう.糖分は幸せになれます.
  • 将来への不安は丸めて何処かにポイしましょう.テキトーに誰かに愚痴るのも良いです.
  • もっと周りにちゃんと目を向けましょう.自分が思っているより自分のことを気にかけてくれている人は案外多いです.
  • 音楽を流しましょう.なんとなく落ち着く感じがあります.
  • 辛くなったらひたすら寝ましょう.睡眠不足は鬱の元です.
  • ココアを飲みましょう.追い詰められているときに飲むと急に視界が開ける感覚を味わえます.もしかしなくても薬物の類では?

こちらに来てからいろんなことが上手く回らず,人生って難しいなぁって感じになっています.
でも頑張りすぎるのは良くないとわかったので,ほどほどに,ゆるーく生きていきたいです.

Bug Shooting Challenge #2 に参加してきました

f:id:NAMEKO:20190303021857j:plain

こんにちは.なめこです.

2019/03/02にmixiの開催するワークショップBug Shooting Challenge #2(以下BSC)に参加してきたのでその概要や感想など色々書きます(途中途中に飯テロが挟まっています).

BSCとは

株式会社ミクシィ主催の学生対象の不具合調査体験ワークショップです.
ミクシィはこの他にGit ChallengeやTDD Challengeといったまた別のワークショップも開催しており,本イベントはそのうちの1つです.

詳細はこちらをご覧ください.

ざっくり説明すると,なんらかのバグが存在しているゲームサーバが与えられ,そのログから不具合の原因の特定・修正に挑戦するイベントです.

経緯

ミクシィの中の人に勧められて言われるがままに応募.
事前課題を極限まで熟成した後に提出しました.

その後無事選考を通過し,お呼ばれしたので参加しました.

準備

プログラムはRuby on Railsで書かれているとのことだったので(というか事前課題の時点でRails),Railsの勉強を1週間かけてやったりやらなかったり諦めたりしてました.

最終的にはMVCモデルに対するざっくりとした知識とrubyの基本構文を携え,一部メタ読みを踏まえて準備が完了しました(何も完了してないですね).

体験記

スケジュールは以下のような感じです.

  • イベントの説明や利用する技術の紹介
  • ランチ
  • 実際に不具合修正に挑戦
  • 懇親会

イベント概要説明・技術紹介

参加者は「2人だけのCREとして不具合調査を行う」という設定です.なので2人1組で協力して挑戦します.

因みになめこはdeltaチームでした.単にA,B,C,...と順にアルファベットを降って良さげな単語を選んだだけだとは思いますが,かっこよくていいですね,delta.

また,サービスの構成に関する説明と使用するツールについてのハンズオンがありました.
Docker,RailsAWSHadoopに関する爆速説明...雰囲気はわかったので良しとしましょう.

そんな感じで前提知識を軽く説明して午前は終了しました.

ランチ

振り返ると大量の釜飯が鎮座していました.

f:id:NAMEKO:20190303010636j:plain

中身はこんな感じでした.とても豪華なランチ...

f:id:NAMEKO:20190303025004j:plain

釜飯の種類が4種類くらいあったのにも驚きました. コンプリートするにはあと3回来ないと...(※BSCは現在1人1度しか参加できません)

ミクシィのエンジニアの方と一緒にランチをしながら色々な話を聞きました.

Challenge

説明を受け,お腹も膨れたところで本題です.

詳細は書けないのでふわっとした感じになりますが,とりあえず不具合調査の流れとしては以下ような感じです.

  1. 問題の内容を元にログを解析する
  2. 手元の環境で再現する
  3. バグの原因を探り,対策する
  4. バグの再現ができないことを確認する
  5. PRを送る

問題は全部で3問ありました.

1問目はどこから手をつければいいかがわからなくて序盤はただあたふたしていました.
チームメンバーがいい感じのログを見つけてきてくれたので,それを元にいろいろしてたら原因が見つかりました. バグを修正しPRの説明文を唸りながら書き上げて提出しました.

2,3問目はツールの使い方にも慣れが出てきたので2人で若干の役割分担をしつつ進めていきました. 紆余曲折はありましたがなんとか時間ギリギリで滑り込みPRできました.

とりあえず完答できたのでよかったです.

回答の採点後,総合MVPと各問題のMVPが発表されました(時系列的には懇親会の後半).

総合MVPは取れませんでしたが,2問目でのMVPをいただきました.
ぎりぎり首の皮一枚繋がってるような状態で回答していたので選ばれるとは思ってませんでしたが,講評を聞くとなるほどなぁと.

懇親会

ランチに引き続き懇親会も豪華でした.

f:id:NAMEKO:20190303031817j:plain

因みに冒頭の写真は懇親会で出たマカロンです.スイーツの類も中々豊富だったので満足です.

結構いろんな輪に入って多くの人と話せたんじゃないかと思います.(当社比+80%).
他の参加者やミクシィの中の人と色々と話をすることができ,技術の話や進路の話,仕事の話などをたくさんしました.
世間は広かったり狭かったりするなぁと感じました.

感想

何度かアプリ開発系のコンテストに参加したことがある程度で,こういったイベントへの参加は初めてでしたがとても充実した内容だったように思います.

単なるバグの修正の経験だけに留まらず,同種のバグを生み出さないために何を意識して開発を行うべきかや,バグが発生した場合のユーザー対応など,普段の個人的な開発やプロトタイプレベルのアプリ開発では目を向けてこなかった面がまだまだあることに気づくことができました.

具体的な課題としては,体系化された知識を正しくつけておくべきだと感じました.その場しのぎの開発は現状のスキルでも十分可能ですが,保守の面から見た際の管理のしやすさを考えた時,今の自分の付け焼きの知識だけでは不十分だと思います.プロダクト全体を俯瞰して見るためにはやはり十分な知識が必要です.

たった数行程度の間違いや不足であっても,ユーザーや会社に多大な損失を与えてしまう可能性があります.現実問題としてバグの存在しないプログラムを作ることは不可能ですが,それでも可能な限りバグを少なく,あるいは損失を小さくできるように常日頃から意識していければと思います.

謝辞

まずはチームメンバーの@ww_furu_tu に感謝を.ログ解析やバグ再現の面で特にお世話になりました.

またイベントの開催・紹介・運営をしてくださったミクシィの方々,本当にありがとうございました. 今回のイベントを経て見えてくるものは多く,自身の大きな糧となったと思います.重ねてお礼申し上げます.

最後に

曰く,「ブログを書くまでが遠足(BSC)」とのことです.

どんなものであれ,自身の経験をアウトプットしていくことは大事ですのでこれからも続けていきたいです.

蛇足

今回1問目と2問目の間に「もぐもぐタイム」なるものがあり,お菓子とドリンクをもらって話を聞きながら食べてました.

f:id:NAMEKO:20190303025535j:plain

やっぱり糖分摂取は必須ですよね.

画像処理.js -エッジ検出 3

こんにちは,なめこです.

jsでの画像処理3回目です.
前回の記事はこちらになります.

nanameko.hatenablog.com

今回はガウス関数を使ったエッジ検出を2種類実装していきます.

2つ目は普通のエッジ検出とはちょっと毛色が違う感じです.

DoG(Difference of Gaussian)

アルゴリズム

その名の通りGaussianの差分を元にエッジを検出します.

概要としては簡単で,分散の違う2つのガウシアンフィルタを適用した画像の差分を取り,その差が大きいものをエッジとします.

参考までに以下にグラフを示します.

f:id:NAMEKO:20190224181241j:plain

赤と青が分散の違う2つのガウシアン関数,緑がその差をとったグラフです.

ガウシアンフィルタはフィルタの総和が1になるように調整されているので,差分を取ると総和は0となります.
周辺画素の値と対象画素の値の差が小さい(エッジがない)と0に近づき,大きい(エッジがある)と適用後の値も大きくなります.

実装

let sig1 = 1.3;
let sig2 = 3.2;

const filterSize = parseInt(sig2*4+1);
const filterSizeHalf = (filterSize-1)/2;

function gaussian(sig, x, y){
    return math.exp(-(x*x+y*y)/2/sig/sig)/2/math.PI/sig/sig;
}

// DoGフィルタの作成
const filter1 = Array.from(new Array(filterSize)).map((v, i) =>
    Array.from(new Array(filterSize)).map((v, j) => 
        gaussian(sig1,i-filterSizeHalf,j-filterSizeHalf)
        ));
const filter2 = Array.from(new Array(filterSize)).map((v, i) =>
    Array.from(new Array(filterSize)).map((v, j) => 
        gaussian(sig2,i-filterSizeHalf,j-filterSizeHalf)
        ));

// 各フィルタの総和計算
const sumFilter1 = filter1.reduce((pre ,cur) => pre+cur.reduce((pre, cur) => pre+cur, 0),0);
const sumFilter2 = filter2.reduce((pre ,cur) => pre+cur.reduce((pre, cur) => pre+cur, 0),0);

// フィルタを正規化しつつ合成
const filter = map(filter1, (v, i, j) => v/sumFilter1-filter2[j][i]/sumFilter2);

// DoGフィルタ適用
const imgDog = conv.conv(img, image.sizes, filter, [filterSize,filterSize], {mode:conv.EXPAND});

// abs
const maxVal = max(imgDog);
const imgDogAbs = map(imgDog, v => v<0?0:(v/maxVal*255));

// 2値化
const imgDogAbsBin = bin(imgDogAbs, {threshold:20, maxVal:255});

2つのフィルタの正規化処理があるので少し長いです.

計算量を減らすために,別々にフィルタをかけてから差分を取るような方法ではなく,合成したフィルタをかけるようにしてあります.

フィルタ適用後の負の値を使用すると一つのエッジに対して複数回反応する(フィルタの両側に負値があるため)ので,負の値は切り捨てています.
また最大値が255となるような正規化も同時に行なっています.

結果を見やすくするために2値化処理も加えました.

実行結果

DoGフィルタの適用結果です.

計算量や実装の難しさはSobelフィルタ程度ですが,陰影への反応も少なく,主要なエッジは綺麗に検出できています.
ただしCanny法と同様にジグザグやグラデーションがかかってるようなエッジに関しては検出できていません.

f:id:NAMEKO:20190224160226p:plain
結果

f:id:NAMEKO:20190224160303p:plain
2値化したもの

FDoG(Flow-Based Difference of Gaussian)

FDoGはこちらの論文にある手法の一部です.

http://www.cs.umsl.edu/~kang/Papers/kang_tvcg09.pdf

アルゴリズム

DoGは等方性によって,エッジが途切れやすいなどの問題があります.
FDoGはエッジのベクトル方向を加味することでより綺麗なエッジ検出を可能とします(Sobelフィルタに対するCanny法のようなものです.たぶん).

ちょっと長くなるので今回は詳細は省きますが一連の流れとしては以下のような感じになります(詳細説明は全体の実装をした際にまとめてします).

  1. ノイズ除去
  2. エッジの接ベクトルを元にした流れ場(ETF)を求める
  3. 直交ベクトル方向に1次元DoGフィルタを適用
  4. ETFの曲線に沿って1次元Gaussianフィルタを適用

ノイズ除去にはGaussianフィルタを使用します.

2ではSobelフィルタでETFの元となるベクトル場を求め,ETFを求める処理を3回ほど繰り返します.

接ベクトルの直交方向にDoGフィルタをかけることでより効率的なエッジの検出ができます.
また,接ベクトル方向の曲線に沿ってGaussianフィルタをかけることで途切れているエッジをつなげることができます.

3,4の処理は複数回繰り返すことで結果が良くなるのでこちらも3回ほど繰り返します.

イラスト風の画像となるという特徴を持っており,処理後の画像も通常のエッジ検出とは違い,白地に黒で描かれたものになります.

実装

// 関数定義
const G = (r, sig) => math.exp(-(r*r)/2/sig/sig)/math.sqrt(2*math.PI)/sig;
const Wm = (g, g_) => (1 + math.tanh(g_ - g))/2;
const Wd = (tx, ty, tx_, ty_) => tx*tx_ + ty*ty_;

// 定数定義
let sigM = 2.5;
let sigC = 1.1;
let tau = 0.5;
let range = 3;

const sigS = sigC*1.6;
const rho = 0.997;

const T = parseInt(sigS*2+0.5);
const S = parseInt(sigM*2+0.5);

// 画像データ取得
const image = cv.imread(argv[0], cv.CV_8UC1);
const img = image.getDataAsArray();

const [height, width] = image.sizes;

// ノイズ除去
const imgGaus = gaussianBlur(img, {kSize:3});

// sobelフィルタ
let {sobelImg:g, sobelX:gx, sobelY:gy} = sobel(imgGaus);
sobelImg = normalize(g);
map(g, (v, i, j) => {
    if(v == 0)return;
    gx[j][i] /= v;
    gy[j][i] /= v;
    return;
});

// gx,gyを90度回転したものをtx,tyの初期値とする
let tx = map(gy, v => -v);
let ty = gx.concat();

// 正規化
g = normalize(g);

// ETF計算を複数回回すループ
for(let n=0;n<3;n++){
    console.log(`Edge Tangent Flow ${n} iteration`);

    const tx_cur = tx.concat();
    const ty_cur = ty.concat();

    // ETF計算
    map(g, (v, i, j) => {
        let tx_sum = 0;
        let ty_sum = 0;

        const tx_num = tx_cur[j][i];
        const ty_num = ty_cur[j][i];

        for(let l=-range; l<=range; l++){
            const j_ = j+l;
            if(j_ < 0 || j_ >= height)continue;
            for(let k=-range; k<=range; k++){
                const i_ = i+k;
                if(i_ < 0 || i_ >= width)continue;
                
                // 対象外領域は弾く
                const r = math.sqrt(k*k+l*l);
                if(r > range)continue;

                // 重み計算
                const w = Wm(v, g[j_][i_])*Wd(tx_num, ty_num, tx_cur[j_][i_], ty_cur[j_][i_]);

                tx_sum += tx_cur[j_][i_]*w;
                ty_sum += ty_cur[j_][i_]*w;
            }
        }

        // 正規化
        const r = math.sqrt(tx_sum*tx_sum + ty_sum*ty_sum);
        if(r != 0){
            tx_sum /= r;
            ty_sum /= r;
        }

        // tx,tyを更新
        tx[j][i] = tx_sum;
        ty[j][i] = ty_sum;
    });
}

// gx,gyを更新
gx = ty.concat();
gy = map(tx, v => -v);

// 定数定義
const THRESHOLD = math.cos(math.PI/8);

let He = img.concat();

// FDoGを複数回回すループ
for(let n=0;n<3;n++){
    console.log(`Flow Based DoG ${n} iteration`);

    // 直交方向に1次元DoGフィルタをかける
    const Hg = map(He, (_, i, j, img) => {
        const x = gx[j][i];
        const y = gy[j][i];
        if(x==0 && y==0)return 0;

        let Csum = 0;
        let Ssum = 0;
        let GCsum = 0;
        let GSsum = 0;

        // 角度に応じて探索方向を定める
        let dk = 0;
        let dl = 0;
        if(math.abs(x) < THRESHOLD){
            dl = 1;
        }
        if(math.abs(y) < THRESHOLD){
            dk = 1;
        }
        if(x*y < 0){
            dl = -1;
        }

        // DoG計算
        for(let l=-T; l<=T; l++){
            const di = l*dk;
            const dj = l*dl;
            const i_ = i + di;
            const j_ = j + dj;
            if(j_ < 0 || j_ >= height || i_ < 0 || i_ >= width)continue;

            const r = math.sqrt(di*di + dj*dj);
            gc = G(r, sigC);
            gs = G(r, sigS);
            GCsum += gc;
            GSsum += gs;
            Csum += img[j_][i_] * gc;
            Ssum += img[j_][i_] * gs;
        }
        return Csum/GCsum - rho*Ssum/GSsum;
    });

    // 流れ場に沿ってGaussianフィルタをかける
    He = map(Hg, (v, i, j, img) => {
        let x = tx[j][i];
        let y = ty[j][i];
        // 流れがなければ白地
        if(x==0 && y==0)return 255;

        sum = v;
        Gsum = G(0, sigM);

        // 接ベクトルの順方向と逆方向に探索
        for(let m=-1; m<2; m+=2){
            let r = 0;
            let i_ = i;
            let j_ = j;

            while(r < S){
                let di = 0;
                let dj = 0;
                if(math.abs(x) < THRESHOLD){
                    dj = m;
                }
                if(math.abs(y) < THRESHOLD){
                    di = m;
                }
                if(x*y < 0){
                    dj *= -1;
                }
                j_ += dj;
                i_ += di;
                if(j_ < 0 || j_ >= height || i_ < 0 || i_ >= width)break;

                r += math.sqrt(di*di + dj*dj);
                const g = G(r, sigM);
                Gsum += g;
                sum += img[j_][i_]*g;

                x = tx[j_][i_];
                y = ty[j_][i_];
                if(x==0 && y==0)break;
            }
        }

        sum /= Gsum;

        // 閾値処理
        return (sum<0 && (1 + math.tanh(sum)) < tau) ? 0 : 255;
    });
}

実行結果

実装時の定数をそのまま使用した場合の結果が以下になります.
DoGフィルタでは検出できなかったジグザグとしたエッジやグラデーションのかかった部分もこの手法では綺麗に検出できています.
またイラスト風というだけあって,陰影も表現されているのが面白いです.

f:id:NAMEKO:20190224160047p:plain
tau=0.5

比較のためtauを0.1, 0.9としたものを示します.tauを小さくすると陰影などの線が少なくなります.

f:id:NAMEKO:20190224160121p:plain
tau=0.1

f:id:NAMEKO:20190224160139p:plain
tau=0.9

まとめ

手軽に綺麗なエッジを取れるDoGとその改良版のFDoGを実装しました.

FDoGは処理は若干複雑ですが,ETF計算以外はガウス関数しか使っていないのが面白いです. またFDoGは自然画像に対して実行すると他の手法とは少し違った特徴が出てくるので試してみるといいかもしれません.

今回の実装含め,これまでのエッジ検出のプログラムは以下のGitHubのedgeディレクトリに置いてあります.

github.com

今回で一旦エッジ検出はクローズとなります.また面白いものが見つかったら書くかもしれません.

次回からは劣化画像修復に移る予定です. 今回までは畳み込みや対象画素付近の探索がメインでしたが,次からは行列計算がメインになります.

参考文献

【画像処理】DoGフィルタの原理・特徴・計算式 | アルゴリズム雑記

PC以外でもJSがしたい

こんにちは,なめこです.

今回はちょっとした思いつきに従ってReact Nativeで遊んでみます.

React NativeはiOSAndroid端末で動くアプリをJavaScriptで作れるフレームワークです.

以下の記事に構成に関する説明がされてます.

qiita.com

さて,まあ構成に関しては軽く流してもいいのですが,今回注目したのは以下の文

React Nativeで楽に作るスマホアプリ開発入門(基本編) - Qiita React NativeはWebkitスマホブラウザ)のJavaScriptランタイムで動く(つまり、スマホブラウザ上で動くようなコードを書いているイメージ)

要するに端末上でJSが動いてるわけですね.
それならアプリ上で任意のJSプログラムを動かすことも可能なのでは? と思い立ってしまったので挑戦してみることにしました.
上手くいけばタブレットなどでもNodeのプログラム実行ができるはずです.

今回はひとまずターミナルでのNodeの対話環境に近いものを目指します.

import周りとかstyle sheetに関する部分は本筋ではないので省略します. まとめたプログラムは最後に置いておきます.

前準備

ターミナル上で必要となる表示をstateとして用意しておきます. 入力と出力を履歴含めて保持できれば十分なのでstateの中身は少ないです.

this.state = {
  // 入力された文字列
  inputValue: '',
  // 入力履歴と出力のリスト
  list: [],
  // 入力履歴のリスト
  historyList: [],
};

JSが動くようにする

デフォルトで生成されるAppクラスの内部に関数_onPressを定義します. これは入力決定時(現時点ではENTERボタンを押した時)に実行されます.

そしてこの中で悪魔の関数を呼び出します. 文字列をJSのプログラムとして評価し,実行するevalさんです.
ある意味で便利ではありますが,危険度の高い関数でもあるのでご利用は計画的に.

evalの詳細は以下

developer.mozilla.org

_onPress = () => {
    // 現在のstateを取得
    const {inputValue, list, historyList} = this.state;

    // 入力文字を実行し,実行結果を取得
    const result = eval(inputValue);
    
    // 実行コードと結果を追加
    const _list = list.concat();
    const _historyList = historyList.concat();
    _list.push(inputValue+'');
    _list.push(result+'');
    _historyList.push(inputValue+'');

    // stateを更新
    this.setState({
      inputValue:'',
      list:_list,
      historyList:_historyList
    });
  };

全体を含む実行画面はこんな感じです.
ひとまずevalは機能しているようです.

f:id:NAMEKO:20190220233244p:plain

変数が保持されるようにする

先ほどの入力状態から以下のような入力をするとエラーが出ました.

f:id:NAMEKO:20190220233251p:plain

f:id:NAMEKO:20190220233254p:plain

_onPress内のevalで変数aにアクセスしたことでエラーが出てます.
原因は割とお察しですが一応varなしでの挙動も確認しておきましょう.

f:id:NAMEKO:20190220233257p:plain

案の定普通に動きました. varなどの宣言子を用いて変数宣言を行った場合,関数処理後にそのスコープを抜け,その場で宣言されたローカル変数は消えます. しかし何もついていない場合,グローバル変数として作られるので変数が残ります.

今回はvarで宣言した変数aが消えているので,ないものにアクセスしようとしてエラーになってるわけです.

varの他constやletも同様にエラーとなるので,とりあえず気にせずできるように今はこれらの文字列を消しておきます.

文字列の変換関数_encodeを定義してevalの実行前に呼び出すようにします.

// '(var|const|let) 'を削除
_encode = (str) => str.replace(/(var|const|let) /g, '');
// 入力文字を実行し,実行結果を取得
const result = eval(this._encode(inputValue));

f:id:NAMEKO:20190220233301p:plain

これで無理やりではありますがとりあえず動くようになりました.

文字列が使えるようにする

これで変数周りは一通り完了...だと良かったのですがまだ問題がありました.

f:id:NAMEKO:20190220233304p:plain

文字列を扱おうとこんなプログラムを書いてみると以下のようなエラーが出ました.

f:id:NAMEKO:20190220233308p:plain

\u2018????
と一瞬なりましたがUNICODEの「' (シングルクォーテーション)」ですね.
どうやら入力はUNICODEのようです. 明示的に変換してあげましょう.

_encode = (str) => str.replace(/(var|const|let) /g, '').replace(/(\u2018|\u2019|\u201c|\u201d)/g, "'");

ついでに「" (ダブルクォーテーション)」の方も変換してあります.

f:id:NAMEKO:20190220233312p:plain

これで文字列も使えるようになりました.

もちろんオブジェクトや配列も利用できます.

f:id:NAMEKO:20190220233317p:plain

変数宣言以外での宣言子の文字列を残す

文字列を扱えるようになってから気づきましたが,現状だと「var 」がどこにあっても削除されてしまいます

f:id:NAMEKO:20190221000909p:plain

正規表現をうまく使って文頭にある場合にのみ削除するようにします. 文頭指定だけだと「 var 」みたいな形になると反応しなくなるので,文頭から空白が続いた場合も対象になるようにします.

_encode = (str) => str.replace(/^ *(var|const|let) /g, '').replace(/(\u2018|\u2019|\u201c|\u201d)/g, "'");

f:id:NAMEKO:20190221001353p:plain

これでひとまずおかしな挙動は消えたと思います.

まとめ

今回は対話形式でのプログラミングができるようにしてみました.

変数宣言周りは無理やりなのでもう少しいい方法がないか検討してみますが,しばらくはこのままになりそうです.

少しずつ体裁とか色々整えていって色々できるようにしていきたいです.

プログラム

import React, {Component} from 'react';
import {
  Platform, 
  StyleSheet, 
  Alert,
  Text, 
  TextInput, 
  Button, 
  ImagePicker,
  Permissions,
  View,
  TouchableOpacity,
  FlatList
} from 'react-native';

type Props = {};
export default class App extends Component<Props> {
  constructor(props){
    super(props);
    this.state = {
      inputValue: '',
      // 入力履歴と出力のリスト
      list: [],
      // 入力履歴のリスト
      historyList: [],
    };
  }

  // テキスト入力の反映
  _onChangeText = inputValue => this.setState({inputValue});
  _onPress = () => {
    const {inputValue, list, historyList} = this.state;

    // 入力文字をエンコードした上で実行し,実行結果を取得
    const result = eval(this._encode(inputValue));
    // if(typeof(result) === 'string' || result instanceof String){
    //   result = `'${result}'`;
    // }
    // Alert.alert(result);
    // const result = this._data(this._encode(inputValue));
    // const result = eval(inputValue);
    
    // 実行コードと結果を追加
    const _list = list.concat();
    const _historyList = historyList.concat();
    _list.push(inputValue+'');
    _list.push(result+'');
    _historyList.push(inputValue+'');

    this.setState({
      inputValue:'',
      list:_list,
      historyList:_historyList
    });
  };

  // '(var|const|let) 'を削除,''や""を使用できるようにする
  _encode = (str) => str.replace(/^ *(var|const|let) /g, '').replace(/(\u2018|\u2019|\u201c|\u201d)/g, "'");
  
  render() {
    const {
      inputValue,
      list,
      historyList
    } = this.state;
    
    return (
      <View style={styles.container}>
        <FlatList style={styles.list} data={list} renderItem={({item}) => <Text style={[styles.item, styles.color]}>{item}</Text>} />
        
        <View style={styles.separator}/>
        <View style={styles.input}>
          <Text style={styles.color}>{'> '}</Text><TextInput style={[styles.inputArea, styles.color]} value={this.state.inputValue} onChangeText={this._onChangeText}/>
          <TouchableOpacity onPress={this._onPress}>
            <Text style={styles.color}> ENTER </Text>
          </TouchableOpacity>
        </View>

      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    backgroundColor: '#1D1D1D',
  },
  input: {
    flexDirection:'row',
    height:20,
    margin:10,
  },
  list: {
    flex:1,
    margin:10,
    marginTop: 20,
  },
  item: {
    fontSize: 15,
    textAlign: 'left',
  },
  inputArea: {
    fontSize: 15,
    flex: 1,
  },
  color: {
    color: '#FEFEFE',
  },
  separator: {
    height: 1,
    backgroundColor: '#FEFEFE',
  },
});