やっちのわいわい日記

電通大に編入した元高専生の日記。日記を書きつつ編入のこととか勉強のこととか書こうと思っています。編入とかで質問があるかたは@amhflcl0514にDMくださいお

真中のんさんの魅力について

この記事はプリッカソンアドベントカレンダー17日目の記事になります(遅刻).

adventar.org

さてみなさんプリパラは見てますか?

まだの方はプリパラを見てから人生を歩むようにして下さい.

そんなプリパラですが私は主人公らぁらの妹である真中のんという娘が好きで,要するに私の推しキャラです.

f:id:nishikino3:20191218014441p:plain

かわいいですね〜〜〜〜〜(オタクヴォイス)

1stシーズンでは小学4年生、2ndシーズン以降は小学生5年生として登場する真中のんですが、その容姿や性格やストーリーについて見ていきましょう.

f:id:nishikino3:20191217214553p:plain

2ndシーズンまでの真中のん

2ndシーズンまでののんはサブキャラクター的なポジションであくまでも,「姉(らぁら)に対して常に自分の方が有能である」と言わんばかりの描写がたくさんあったかと思います.

例を挙げると,らぁらのプリチケをタイミングよく隠したり,らぁらよりも先に起きていたりするなど...です.

本人は直接口に出していませんが,基本的にはらぁらよりも自身の方が優れていることが当たり前となっているのだと考えられます.

f:id:nishikino3:20191218014038p:plain

行動原理の一貫性

のんはらぁらに負けるはずが無いから,神アイドルを目指すらぁらを超えるために神アイドルになろうと奮闘します.

3rdシーズン全体を見渡すとのんの全ての行動原理の根幹には「らぁらに負けたくない」という想いで,トライアングルとして闘い,グランプリに挑み,チームを結成します.

f:id:nishikino3:20191218014821p:plain

3rdシーズンでの真中のん

個人的に3rdシーズンを通して一番苦悩し,成長したのはのんで実質的な主人公だと考えています.

勘違い

3rdシーズンの前半ではのんが自身で三役をこなす人間離れした才能を見せつけて神アイドルグランプリに臨みます.

この時点で一人三役という離れ技自体は明らかにらぁらを超越してる才能だと言えます.

しかしのんはチームに対して

  • 完璧なチームを組めば良い
  • じゃあ私が全部やればよくない?

という考えからトライアングルを結成しており,これはチーム≠友達 として捉えてしまっている点がらぁらたちと明らかに異なっています.

あくまでものんが「らぁらに負けたくない」という想いから,のんが考えた最強のチームを作った結果であり,プリパラのテーマでもある「ともだち」とは違う考え方です.

f:id:nishikino3:20191218013800p:plain

挫折

結果としてグランプリではソラミとドレシに敗北してしまい,(らぁらに負けるつもりがなかったため)負けゼリフを用意しておらず一人三役が周囲にバレてしまいます.

この敗北でのんが自身の力では勝てないことが分かり,単なる最強チームではなく,ともだちの力が必要だということに気が付き,チーム結成に臨みます.

ここでもあくまでもチーム結成の動機が「らぁらに負けたくない」とも受け取れます.

f:id:nishikino3:20191218015359p:plain

チーム結成

チーム結成にはマネージャーのウサチャのアドバイスもあり,ちりとペッパーで組もうと考えますがなかなかうまく行きません.

ちりとペッパーとのチームを結成の決意となる出来事は,月川家に殴り込んだ際にちりの生花を3人の力で達成したことで,のんはウサチャのアドバイスが本物であると確信してチーム結成します.

f:id:nishikino3:20191218013853p:plain

成長

ここまでをまとめると3rdシーズンでののんの変化(成長)をまとめると

  • らぁらに勝てるチームを組みたい
  • じゃあ私が考えた最強の3人を作れば良い
  • それって私3人
  • らぁらに勝てない(のん的に例外パターン)
  • チームではなく友達の力が必要だと気がつく
  • ノンシュガーの三人ならできるような気がする!!

容姿・性格

  • かわいい
  • 好き
  • 声も好き

f:id:nishikino3:20191218004153p:plain f:id:nishikino3:20191218004133p:plain f:id:nishikino3:20191217214446p:plain

真中のんというキャラクターのどこが好きか

では,以上を踏まえて私自身が真中のんのどこに魅力を感じ,好きな理由を分析した結果が以下の通りです

  • 「姉に負けたくない」という強い意志と,そのために突き進む真っ直ぐさ
    • 私(筆者)自身が弟で兄に対して対抗心を持っていたため共感ポイント爆上がり
  • 自分一人で3人チームを組めちゃう実力
    • 普通にすげぇぇってなった
  • アイパラの最後で真っ先に起こそうとしたり一番悲しんでる様子
    • ここであああああああああってなった
  • 顔がいい
  • 声がいい
  • かわいい
  • かわいい

最後に我が家の真中のんグッズをまとめたやつです↓

f:id:nishikino3:20191218015740j:plain

グッズ集めるの楽しいいいいいいいいい!!!

apple watchつけて走る

この記事はwhywaita Advent Calendar 2019 - Adventar 4日目の記事です。

adventar.org

さてさてwhywaitaさんと言ったらやっぱり「運動」というワードは非常に大きな要素になるんじゃないかなって思います. whywaitaさんを見ていると如何に運動の重要さを考えさせられますね. ちなみに私はwhywaitaさんの所属していた研究室の後輩で2年間同じ空間で苦楽(?)を共にした仲だったりします.

そこで今回は最近apple watchつけて走っいるのでその様子や率直な感想を紹介したいと思います. 私がもっているのは Apple Watch Series 4のGPSモデルです.

走ってる理由

正直ちょっと前まで長距離を走るのはアホらしかったですが,持ち腐れていたApple Watchをどうにか有効活用したい! と思い,そういえばApple Watchでワークアウトを記録できたな〜とぼーっと思い走ることにしました.

あともう少し痩せたいな〜ってのもあります

収集できるデータ

ちゃんと機能とか調べてなかったのですがいざ使ってみるとかなり高機能で感動しました. (機能とか予め調べよう😇)

↓が実際にアプリで閲覧できるデータです. f:id:nishikino3:20191204224721p:plain

走行距離

最初これなんでほぼ正確に取れてるの!?って疑問に思いましたがGPSとれるならそりゃ計算できますね.

距離だけ決めて適当に走るときは,走りながら確認も簡単にできるので折返しなどの目安とかいなるのでめちゃめちゃ便利です.

合計時間

どれくらい長く走ったかって分かると距離もそうですけど時間も結構達成感があったりします.

消費カロリー

これ見ると走ってよかった!!って気がしてきてモチベーションが上がります.

平均ケイデンス

走るときの足の回転です(多分)

走り方を変えてみてピッチを上げたほうが楽とかそうじゃないとか分かるので走るのに慣れてくると案外たのしいです

平均心拍数

あードキドキしてるって感じですね(適当)

f:id:nishikino3:20191204224804p:plain

平均ペース

これは1kmあたりの平均ペースになります!

同じコースを走るのであれば以前よりも早くしよう!!ってなるのでジョギングでも張り合いが出て楽しいです!

ラップタイム

1kmごとのラップタイムです.

やっぱり最初とばしすぎたな〜とか中盤でどんどん遅くなっちゃうな〜とか感覚では分からないので,走るペースの改善にもなります

f:id:nishikino3:20191204224824p:plain

走行マップ

知らないところを走って地図を見ると結構感動しますし,走るようになってから今まで行ったことない場所にも行くようになりました.

まとめ

実際に記録しながら走ると取れるデータがすごく有用で,最近では走ったらアプリのスクショをツイートするくらいにハマってしまいました.

以前までApple Watchを持ち腐れてたましたが今では大切なパートナーです.

ちなみに最近はリングフィットアドベンチャーにもハマっていて,ランニングかリングフィットか筋トレのどれかを毎日する生活を送っています!

もしあなたがApple Watchを持ち腐れているなら一回装着して運動してみてはいかがでしょうか?

私はwhywaitaさんの健康を切に願います🙏

おわりに

偉大な先輩のアドベントカレンダーに参加できてすごく光栄です! (明日の執筆者が未定で若干不穏ですが)

ここまで読んで頂きありがとうございました!

プリパラ1話のアバンが如何に完成されているか解説してみる

プリッカソン Advent Calendar 2019 2日目の記事です.

adventar.org

プリパラ1話のアバンが如何に完成されているか解説していこうかなって思います.

作品の解説もある意味ハックなのかもしれませんね.

アバン(OP前)の完成っぷりぷり

プリパラと言ったらアバンの情報量が毎話すごいことで有名です(個人的に).

アバンではその話に何をするかという情報を端的に伝えるものであると自分は考えます. 論文で言うところの概要にあたるものだと思います.

特に1話のアバンではアニメ全体の冒頭にあたるため,視聴者を如何に引き込むか重要になってきます.

アバンでしていること

f:id:nishikino3:20191202122542p:plain

さてさて本題のプリパラ1話のアバンを,アニメを見る中(前提知識なし)でわかる情報をざっくりまとめてみます.

  • 〜10s:テレビに映った3人の少女のライブ映像を視聴
  • 〜13s:そのライブ(アイドル)に対して憧れを抱く少女
  • 〜15s:別の少女(のん)によるそのライブが3年前の伝説のラストライブの映像という解説
  • 〜23s:ダンスや衣装に対する憧れ
  • 〜27s:早くその少女に朝食を食べろと叱る母親
  • 〜37s:「大声」に反応して母親に怒る少女と,さらに怒り返す母親そのまま口喧嘩(父親は気にせず朝食を食べている)
  • 〜41s:パパのパスタという看板のカット
  • 〜45s:すでに学校に出発しているのん
  • 〜57s:ピザを食べながら,セインツ(伝説のアイドル)に対する憧れを口にするが自分には絶対無理であると発言し学校へ出発
  • 〜90s:それまでの雰囲気と全く違うBGMで様々な少女に謎のタイミングで届くカードのようなもの
  • OP

アバンから視聴者が得られるもの

さて先程はアバンでなにをしているか列挙しただけですが,ここではその情報から得られる情報や,感情についてまとめていこうと思います.

主人公と思われる少女の人物像

  • アイドルに対する憧れ
  • あくまでもアイドルは憧れの域から出ない
  • 朝からピザを食べている
  • 声が大きい

主人公と思われる少女の家族・自宅

  • 父・母・妹・主人公の4人構成と思われる
  • よい家庭環境
  • レストランを経営している
  • 妹の方がしっかりしていると思われる

謎の世界観

  • 少女にチケットが急に届く
  • アイドルがすごく流行ってそう

ざっくりと以上の情報を受け取れると思います.

この時点で視聴者は主人公に対して多くの愛着を感じるかなと思います. そのポイントとなっていると考えられるシーンが

  • 食事
  • 家庭
  • 性格

の描写です.

要するに日常描写です.これによって主人公の少女は視聴者と同じように(実際は違うが)生活しているということが分かります.

これは経験論なのですが,視聴者がキャラクターに愛着を感じるのはこういった日常の細かい描写の積み重ねだと考えます. 愛着が湧いたキャラクターはその後の行動背景などが分かるため応援しやすくなる気がします.

さらに謎のチケットが急に届く世界観は落ち着いて考えたら興味が湧くので期待に胸を膨らませて視聴することができます.

まとめ

つまりこの90秒のアバンだけで

  • 主人公に対する愛着
  • 世界観への期待

を達成しているのです

4年間を共に過ごす少女に対してこれを1話の冒頭90秒で成し遂げているのって落ち着いて考えたらすごいな〜って気がします

この記事では1話のアバンだけを解説しましたが1話自体の完成度も高く,

  • 1話の中だけでさらに主人公に対する愛着が増す描写が多数
  • 分かりやすく解説される世界観
  • 今後の問題がたくさんあるにも関わらず期待で胸の膨らむ展開
  • 他にもたくさんいる個性的なキャラクター
  • etc...

というようなことが1話から分かります.

ここでは長くなるので1話全体の解説は割愛させていただきますが,コードを書くだけでなくこのように作品自体のハックをしてみるのもまた一つ面白みがあります!

既にプリパラ見た方も見てない方もこの記事を読んで少しでもプリパラを視聴しよう!ってなってくれたら幸いです.

ここまで読んで頂きありがとうございました.

PS

地味にアドベントカレンダー初です

オタクくんさぁ...インディビジュアル・ジュエルの2番さぁ...

f:id:nishikino3:20191117171730p:plain

オタクくんさぁ...

プリチャンのまりあちゃんとすずちゃんがなんであんな感じの関係だったのか色々考えようと思ってさぁ...

リングマリィのさぁ...インディビジュアル・ジュエルで考えたんだけどさぁ...

1番Aまりあ

私のスキ 君の正義じゃないみたい
それでもダイスキ 君のことがダイスキ

1番Aすず

My very very かっこいい
君の正義じゃないみたい
それでもいいんだ だからこそ溢れた
トゥインクル・フィーバー かっこかわいいパワー
惹かれ合った 光り合った今!

1番B

違う君と認め合いたい
同じ夢を見たいEye-to-Eye
すべて叶えられそう
...

1番のABでは結成前のお互いに対する本当の気持ちを表していると思うんだよね. お互い違くても惹かれ合うし認め合えるそんな予感しかなかったんだと思うんだよ.

で,問題の2番なんだけど

2番A

「わ〜お♡」めちゃめちゃめっちゃかわいい
「も〜お♡」あれこれ全部かわいい
(まりあ) 私の世界にきた
「わ〜お♡」未確認生命体
「も〜お♡」もっともっともっと知りたい
(まりあ)と思うわけ ちょっと考える♡

2番B

Now It's me!
My very veryかっこいい世界にやってきた君
毎日おどろくの「かわいい♡」
ある日あるとき強引にふたりが出会った意味
新しい楽しい予感がするよ!
かっこいい かわいい(かっこいいね)

2番のAメロは1番と違って2人で歌ってるのも単純にいいんだけどさぁ...

Aを2人で歌っているっていうことは,Aの歌詞っていうのは最初から2人とも同じ気持ちだったという解釈ができると思うんだよ...

つまり最初からお互いの知らない世界(かわいいかっこいい)に対する興味自体は存在してたってことだと思うんだよねぇ...

まりあちゃんはかっこいいを知らないからかわいい(自分の知っている世界)という表現で突っ込んでるのだと思うんだよ...

一方ですずちゃんは知らないもの(かわいい)を押し付けられる事に恐怖して逃げるしかできなかったんだよね...

普通知らない世界のものを押し付けられたら怖くなってそうなるじゃん だからすずちゃんの序盤のまりあから逃げる行動はすっごく納得いくし共感できるんだよ...

つまりきっかけさえあれば2人は結ばれる運命にあったんだなぁ...っていう気持ちになったんだけど,脳内が乱雑過ぎて文章がまとまらない中ここまで読んでくれてありがとう

ちなみにオタクくんはどう思う訳?

プリッカソン#9に参加しようと思ったけど体調不良で欠席してユメショック

プリッカソン#9に参加するつもりでしたが当日体調不良で自宅で療養してました(涙目の患者)

prickathon.connpass.com

最近は真中のんがかわいくてしんどい

プリッカソンとは

プリティシリーズ(プリティーリズム/プリパラ/プリチャン)を技術の面から支えていきたい,そんな思いから生まれたハッカソン

要するにエンジニア女児による技術イベントですが、プリッカソン自体はコンテンツが好きなら誰でもウェルカム!雑談も含めてなんでもやってみよう!みたいなスタンスで優しい会となっています

最近はイラストを書く人やハードウェアっぽいことや原稿書いてる人もいてコンテンツが好きなら何でもこい!!って感じで好きです

せっかくなのでそろそろ友人にらぁらちゃんのリボン型のアンテナ作って特性を計測してもらいたいです

今回も主催の @takanakahiko さんはじめ運営の方ありがとうのゆめかわ!(行けなくてすまん)

当日

朝起きたらとてつもない頭痛と全身の倦怠感に襲われて起き上がることができませんでした😇

やろうと思ってたこと

一応考えていたことはあってそれが

ニコ動のプリチャンBBシリーズタグの更新状況を確認して新着があったらslackに通知する

というものを作ろうと思ってました

  • プリチャンBBシリーズとは...↓動画を見て

www.nicovideo.jp

ニコ動には公開してるAPIがあるっぽいのでそのへんを活用してRubyとか使って実装しようかな〜ってぼんやり考えてました

あと時間があれば前回まで引き続き作ってたキャプ画像DBの機能追加とかを考えてました

希望の光

今回なんと @_pkpq_ さんが発表の配信アプリを作って頂いたおかげで当日の発表のスライドをリアルタイムでみることができました!!!

本当に助かりました!!!!めちゃくちゃ良かったです!!!

でもやっぱりデモ見たりオタクと会話したりみらいちゃんと会いたかったので次回こそは現地に行きたいです!

最後に

Elixir勉強日記6 〜モジュール編〜

Elixir勉強日記6回目です

前回で関数まわりに触って若干お腹いっぱいになってサボり過ぎました

続きを進めます

f:id:nishikino3:20190611145254j:plain

elixirschool.com

モジュール... defmodule的な?

モジュール

私たちは経験的に、全ての関数を1つの同じファイルとスコープに持つと手に負えないことを知っています。 このレッスンでは関数をまとめ、構造体として知られる特別なマップを定義することで、コードをより効率のよい形に組織化する方法を取り上げます。

スコープを切り分けるためにあるんですね(情弱)

モジュール

モジュールは関数群を名前空間へと組織する最良の方法です。 関数をまとめることに加えて、関数のレッスンで取り上げた名前付き関数やプライベート関数を定義できます。

落ち着いて考えたらクラスないしないと困りますね

基本的な例を見てみましょう:

defmodule Example do
  def greeting(name) do
    "Hello #{name}."
  end
end

iex> Example.greeting "Sean"
"Hello Sean."

Elixirではモジュールをネストすることが可能で、機能ごとにさらなる名前空間をつけることができます:

defmodule Example.Greetings do
  def morning(name) do
    "Good morning #{name}."
  end

  def evening(name) do
    "Good night #{name}."
  end
end

iex> Example.Greetings.morning "Sean"
"Good morning Sean."

.でネストの表現するんですね〜(Rubyだと:

モジュールの属性(attribute)

モジュール属性はElixirでは一般に定数として用いられることがほとんどです。 単純な例を見てみましょう:

defmodule Example do
  @greeting "Hello"

  def greeting(name) do
    ~s(#{@greeting} #{name}.)
  end
end

どうゆうことだってばよ...

重要なので言及しておきますと、 Elixir には予約されている属性があります。 もっとも一般的なのは以下の3つです:

  • moduledoc — 現在のモジュールにドキュメントを付けます。
  • doc — 関数やマクロについてのドキュメント管理。
  • behaviour — OTP またはユーザが定義した振る舞い(ビヘイビア)に用います。

あーー属性ってのはモジュール内で共通して扱う値のことなんですね

日本語にするとなんか微妙にぱっとしないですね

調べたら属性 => attributesでした

日本語分かりくすぎる

構造体(Structs)

構造体は定義済みのキーの一群とデフォルト値を持つ特殊なマップです。 モジュール内部で定義されなくてはならず、そのモジュールから名前をとります。 構造体にとっては、モジュール内部で自身しか定義されていないというのもありふれたことです。

構造体ってきくとCを思い出しますが...

モジュール内で定義されるマップの属性みたいな感じなんすね

構造体を定義するには defstruct を用い、フィールドとデフォルト値のキーワードリストを添えます:

defmodule Example.User do
  defstruct name: "Sean", roles: []
end

いくつか構造体を作ってみましょう:

iex> %Example.User{}
#Example.User<name: "Sean", roles: [], ...>

iex> %Example.User{name: "Steve"}
#Example.User<name: "Steve", roles: [], ...>

iex> %Example.User{name: "Steve", roles: [:manager]}
#Example.User<name: "Steve", roles: [:manager]>
# 構造体はあたかもマップのように更新することができます:

iex> steve = %Example.User{name: "Steve"}
#Example.User<name: "Steve", roles: [...], ...>
iex> sean = %{steve | name: "Sean"}
#Example.User<name: "Sean", roles: [...], ...>

構造体はマップのように更新できるのね...

パターンマッチングが未だに慣れない...

最も重要なことですが、構造体はマップに対してマッチすることができます:

iex> %{name: "Sean"} = sean
#Example.User<name: "Sean", roles: [...], ...>

Elixir 1.8以降、構造体にカスタムイントロスペクション機能が追加されました。

かすたむいんとろすぺくしょん...

カスタムイントロスペクションがどのように使われるのかを理解するため、 sean の中身を見てみましょう。

iex> inspect(sean)
"#Example.User<name: \"Sean\", roles: [...], ...>"

この例では全てのフィールドが出力対象になっていますが、出力したくない項目がある場合、どのようにしたら良いでしょうか? この場合、 @derive を利用することで実現することができます! roles を出力から除外したい場合、以下のように記述します。

defmodule Example.User do
  @derive {Inspect, only: [:name]}
  defstruct name: nil, roles: []
end
# 注記: @derive {Inspect, except: [:roles]} でも実現することができます。

モジュールを更新したら、 iex で確認してみましょう。

iex> sean = %Example.User{name: "Sean"}
#Example.User<name: "Sean", ...>
iex> inspect(sean)
"#Example.User<name: \"Sean\", ...>"
# roles が出力から除外されました!

モジュールの構造体から値を絞って出力するってことなんすかね

クラスのインスタンス変数のアクセス権的な扱いみたいになってるのでしょうか...?

いやモジュールを直で叩いたら構造体の全部の値出てくるから違うか(普段からinspectでモジュールを呼べってことなのか)

コンポジション(Composition)

さて、モジュールと構造体の作り方がわかったので、コンポジションを用いてモジュールや構造体に既存の機能を追加する方法を学びましょう。 Elixir は他のモジュールと連携する様々な方法を用意しています。

他のモジュールと連携

alias

モジュール名をエイリアスすることができます。 Elixir のコードでは頻繁に使われます:

defmodule Sayings.Greetings do
  def basic(name), do: "Hi, #{name}"
end

defmodule Example do
  alias Sayings.Greetings

  def greeting(name), do: Greetings.basic(name)
end

これを使うことでモジュール間でメソッドのやり取り(継承に近い?)感じなんですね

alias を使わない場合

defmodule Example do
  def greeting(name), do: Sayings.Greetings.basic(name)
end

2つのエイリアス間で衝突があったり、全体を別名でエイリアスしたい場合には、 :as オプションを使います:

defmodule Example do
  alias Sayings.Greetings, as: Hi

  def print_message(name), do: Hi.basic(name)
end

扱いやすい名前で扱えるのはありがたいですね

複数のモジュールを一度にエイリアスすることも可能です:

defmodule Example do
  alias Sayings.{Greetings, Farewells}
end

便利!

import

モジュールをエイリアスするよりも、関数を取り込みたいという場合には、 import を使います:

iex> last([1, 2, 3])
** (CompileError) iex:9: undefined function last/1
iex> import List
nil
iex> last([1, 2, 3])
3

あっ普通はimportつかうんだ..

フィルタリング

デフォルトでは全ての関数とマクロが取り込まれますが、 :only や :except オプションを使うことでフィルタすることができます。

まあ全部使うわけじゃないですしね

pythonとかでよく見かける気がします

特定の関数やマクロを取り込むには、名前/アリティのペアを :only や :except に渡す必要があります。 last/1 で最後の関数のみを取り込んでみましょう:

iex> import List, only: [last: 1]
iex> first([1, 2, 3])
** (CompileError) iex:13: undefined function first/1
iex> last([1, 2, 3])
3

last/1 で指定された関数以外を全て取り込むには、同じ関数で試してみましょう:

iex> import List, except: [last: 1]
nil
iex> first([1, 2, 3])
1
iex> last([1, 2, 3])
** (CompileError) iex:3: undefined function last/1

名前とアリティのペアを渡すのってアリティ指定するのちょっとだるいですね

名前/アリティのペアに加えて、 :functions と :macros という2つの特別なアトムもあります。これらはそれぞれ関数とマクロのみを取り込みます:

import List, only: :functions
import List, only: :macros

どうでもいいけど:macrosってマクロスっぽい

require

他のモジュールのマクロを使用することをElixirに伝えるために require を使うことができます。 import とのわずかな違いは、関数ではなくマクロを使用可能とすることです。

defmodule Example do
  require SuperMacros

  SuperMacros.do_stuff
end

マクロを呼び出すのが require

まだロードされていないマクロを呼びだそうとすると、Elixir はエラーを発生させます。

それはそう

use

use マクロを用いることで他のモジュールを利用して現在のモジュールの定義を変更することができます。 コード上で use を呼び出すと、実際には提供されたモジュールに定義されている using/1 コールバックを呼び出します。 using/1 マクロの結果はモジュールの定義の一部になります。 この動作に対する理解を深めるために簡単な例を見ましょう:

ほかのモジュールを利用して現在のモジュールの定義を変更する...?

defmodule Hello do
  defmacro __using__(_opts) do
    quote do
      def hello(name), do: "Hi, #{name}"
    end
  end
end

マクロについても多少調べたほうがええんかな

とりあえずuseで呼び出すと__using__が呼び出されるって感じなんですね

__using__って名前Pythonに似てる気がしますね

ここでは hello/1 関数を定義する using/1 コールバックを定義した Hello モジュールを作りました。 この新しいコードを試すために新しいモジュールを作ります:

defmodule Example do
  use Hello
end

IExでこのコードを試して見ると Example モジュールで hello/1 を使えるのがわかります。

iex> Example.hello("Sean")
"Hi, Sean"

使えるようになる...???

defmacro __using__ が走ってhelloメソッドが生えてるってことかな

ここで use が Hello の using/1 コールバックを呼び出して、結果のコードをモジュールに追加します。 基本的な例を見せたので、ここからはこのコードを変更して using/1 にオプションをサポートする方法を見てみましょう。 greeting オプションを追加します:

defmodule Hello do
  defmacro __using__(opts) do
    greeting = Keyword.get(opts, :greeting, "Hi")

    quote do
      def hello(name), do: unquote(greeting) <> ", " <> name
    end
  end
end

新しく作った greeting オプションを含むために Example モジュールを更新します:

defmodule Example do
  use Hello, greeting: "Hola"
end

IExで試して見ると挨拶が変わるのを確認できます。

iex> Example.hello("Sean")
"Hola, Sean"

これらは use がどうやって動作するのかを説明する簡単な例でしたが、これは Elixir のツールボックスで信じられないほどに強力なツールです。 Elixir を学び続けたら use をあっちこっちで見ることになるでしょう。かならず見ることになりそうな例をひとつあげれば、 use ExUnit.Case, async: true です。

うーん実際にどう使うと便利なのかイメージがなかなかつかないので、使われているところを見たら考えよう(思考停止)

注意: quote 、 alias 、 use 、 require はメタプログラミングで使用してたマクロです。

まとめ

  • defmoduleでモジュール化できる
  • モジュールで扱うattributesをもつことができる
  • defstructを使うことでモジュールの値を構造体(Structs)としてマップのように扱うことができる
  • モジュール間を色々つなぎこむことができる
  • aliasで別のモジュールのメソッドをコピー?というか扱えるようになる
  • 普通はaliasではなくimportでメソッドとマクロをごっそり取り込む
  • requireでモジュールのマクロを使用可能にする
  • useでモジュールでdefmacro __using__で定義されたマクロが走る

Elixir勉強日記5 〜関数・パイプライン演算子編〜

Elixir勉強日記5回目です

最近はずっとRailsばっかりやっててElixir触るの忘れがちです

引き続き進めて生きます

f:id:nishikino3:20190611145254j:plain

elixirschool.com

今回はついに関数型言語において関数という題で進めることになります...!

5回目まで関数が出てこない関数型言語のドキュメントってある意味素晴らしいですよね

関数

Elixirや多くの関数型言語では、関数は第一級市民(≒ファーストクラスオブジェクト)です。 Elixirにおける関数の種類について、それぞれどう異なっていて、どのように使うのかを学んでいきます

いや関数型言語だから間違ってないけど草

匿名関数

その名前が暗に示している通り、匿名関数は名前を持ちません。 Enumのレッスンで見たように、匿名関数はたびたび他の関数に渡されます。 Elixirで匿名関数を定義するには、fnとendのキーワードが必要です。 これらの内側で、任意の数の引数と->で隔てられた関数の本体とを定義することができます。

iex> sum = fn (a, b) -> a + b end
iex> sum.(2, 3)
5

この辺の記法ってjsやってると違和感ないんでしょうかね

.の存在絶対忘れそう

&省略記法

匿名関数を利用するのはElixirでは日常茶飯事なので、そのための省略記法があります:

iex> sum = &(&1 + &2)
iex> sum.(2, 3)
5

おそらく見当が付いているでしょうが、省略記法では引数を&1、&2、&3などとして扱うことができます。

省略記法いいですけど知らない人見たら結構謎ですね

パターンマッチング

Elixirではパターンマッチングは変数だけに限定されているわけではなく、次の項にあるように、関数へと適用することができます。

Elixirはパターンマッチングを用いてマッチする可能性のある全てのオプションをチェックし、最初にマッチするオプションを選択して実行します:

iex> handle_result = fn
...>   {:ok, result} -> IO.puts "Handling result..."
...>   {:ok, _} -> IO.puts "This would be never run as previous will be matched beforehand."
...>   {:error} -> IO.puts "An error has occurred!"
...> end

iex> some_result = 1
iex> handle_result.({:ok, some_result})
Handling result...

iex> handle_result.({:error})
An error has occurred!

すごい!この分岐なかなか美しいですね!

_が意味するのはこの変数を使わないってことっぽいですかね

2つ目の関数には入らないみたいです

名前付き関数

関数を名前付きで定義して後から呼び出せるようにすることができます。 こうした名前付き関数はモジュール内部でdefキーワードを用いて定義されます。 モジュールについては次のレッスンで学習しますので、今のところ名前付き単体に着目しておきます。

モジュール内部で定義される関数は他のモジュールからも使用することができます。 これはElixirでは特に有用な組み立て部品になります

個人的には関数は名前をつけるものっていう感覚なので別に名前をつけるのが当然っていう感覚はないみたいですね

defmodule Greeter do
  def hello(name) do
    "Hello, " <> name
  end
end

iex> Greeter.hello("Sean")
"Hello, Sean"

関数本体が1行で済むなら、do:を使ってより短くすることができます:

defmodule Greeter do
  def hello(name), do: "Hello, " <> name
end

1行で書けてるけど実際こういう記述ってするんですかね 可読性びみょい気が

パターンマッチングの知識を身にまとったので、名前付き関数を使った再帰を探検しましょう:

defmodule Length do
  def of([]), do: 0
  def of([_ | tail]), do: 1 + of(tail)
end

iex> Length.of []
0
iex> Length.of [1, 2, 3]
3

身にまとったってなんやねんwww

慣れないと気持ち悪い例ですが理解できると美しいですね

これが関数型言語の魅力なんでしょうかね

関数の命名とアリティ

以前言及したとおり、関数は名前とアリティ(引数の数)の組み合わせで命名されます。 つまり、以下のようなことができるということです:

defmodule Greeter2 do
  def hello(), do: "Hello, anonymous person!"   # hello/0
  def hello(name), do: "Hello, " <> name        # hello/1
  def hello(name1, name2), do: "Hello, #{name1} and #{name2}"
                                                # hello/2
end

iex> Greeter2.hello()
"Hello, anonymous person!"
iex> Greeter2.hello("Fred")
"Hello, Fred"
iex> Greeter2.hello("Fred", "Jane")
"Hello, Fred and Jane"

関数名の一覧を上記のコメントに載せました。 例えば、1つめの実装は引数を取らないのでhello/0、2つ目は1つの引数を取るのでhello/1となります。 他の言語におけるオーバーロードとは違い、これらは互いに 異なる 関数として扱われます。 (さっき扱ったパターンマッチングは 同じ 数の引数を取る関数定義が複数ある場合のみ適用されます)

引数の数で異なる関数として認識されるんすね

この辺は他の言語と違うのでしっかり覚えておいた方がよさそうですね

関数とパターンマッチング

※この節ながい

内部では、関数は実行された時の引数をパターンマッチングしています。

マップを受け取るが、特定のキーにだけ関心がある関数が必要であるとしましょう。 私たちは次のようにキーの有無に基づいて引数をパターンマッチすることができます:

defmodule Greeter1 do
  def hello(%{name: person_name}) do
    IO.puts "Hello, " <> person_name
  end
end

関数の受け渡しにパターンマッチング使ってるので色々応用が効きそうですね

この辺りも頭に入れておかないと違和感しかないですね

今度はFredという名前の人物を表すマップを持っているとしましょう。

iex> fred = %{
...> name: "Fred",
...> age: "95",
...> favorite_color: "Taupe"
...> }

Greeter1.hello/1 をfredのマップで実行するとこのような結果となります:

# call with entire map
...> Greeter1.hello(fred)
"Hello, Fred"
# :nameキーを 含まない マップで関数を実行するとどうなるでしょうか?

# call without the key we need returns an error
...> Greeter1.hello(%{age: "95", favorite_color: "Taupe"})
** (FunctionClauseError) no function clause matching in Greeter1.hello/1

    The following arguments were given to Greeter1.hello/1:

        # 1
        %{age: "95", favorite_color: "Taupe"}

    iex:12: Greeter1.hello/1

このような挙動となる理由は、Elixirは関数が実行された際の引数を関数で定義されたアリティに対してパターンマッチさせているためです。

Greeter1.hello/1にデータが届いた際どのように見えるか考えてみましょう:

はぇ〜〜〜〜〜〜〜

# incoming map
iex> fred = %{
...> name: "Fred",
...> age: "95",
...> favorite_color: "Taupe"
...> }

Greeter1.hello/1は次のような引数を期待します:

%{name: person_name}

Greeter1.hello/1では、私たちが渡したマップ(fred)は引数(%{name: person_name})に対して評価されます:

%{name: person_name} = %{name: "Fred", age: "95", favorite_color: "Taupe"}

これは渡されたマップの中にnameに対応するキーを見つけます。 マッチがありました!このマッチの成功によって、右辺のマップ(つまり fredマップ)の中にある:nameキーの値は左辺の変数(person_name)に格納されます。

さて、Fredの名前をperson_nameにアサインしたいが、人物マップ全体の値も保持したいという場合はどうするのでしょう?挨拶を出力した後IO.inspect(fred)を使いたいとしましょう。 この時点では、マップの:nameキーだけをパターンマッチしているので、そのキーの値だけが変数に格納され、関数はFredの残りの値に関する知識を持っていません。

これを保持するためには、マップ全体を変数にアサインして使用できるようにする必要があります。

新しい関数を作ってみましょう:

defmodule Greeter2 do
  def hello(%{name: person_name} = person) do
    IO.puts "Hello, " <> person_name
    IO.inspect person
  end
end

Elixirは引数を渡されたままパターンマッチするということを覚えておいてください。 そのためこのケースでは、それぞれが渡された引数に対してパターンマッチして、マッチした全てのものを変数に格納します。 まずは右辺を見てみましょう:

person = %{name: "Fred", age: "95", favorite_color: "Taupe"}

ここでは、personが評価され、fredマップ全体が格納されました。 次のパターンマッチに進みます:

%{name: person_name} = %{name: "Fred", age: "95", favorite_color: "Taupe"}

これは、マップをパターンマッチしてFredの名前だけを保持したオリジナルのGreeter1関数と同じです。 これによって1つではなく2つの変数を使用することができます:

引数を2つのアリティでそれぞれパターンマッチングしてるのか...

personは%{name: "Fred", age: "95", favorite_color: "Taupe"}を参照します person_nameは"Fred"を参照します これでGreeter2.hello/1を実行したとき、Fredの全ての情報を使用することができます:

この例って関数渡す側はタプルで渡すけど関数側ではタプルの一部の情報を変数でもちたいっていうのが前提にあるって感じなんですかね

# call with entire person
...> Greeter2.hello(fred)
"Hello, Fred"
%{age: "95", favorite_color: "Taupe", name: "Fred"}
# call with only the name key
...> Greeter4.hello(%{name: "Fred"})
"Hello, Fred"
%{name: "Fred"}
# call without the name key
...> Greeter4.hello(%{age: "95", favorite_color: "Taupe"})
** (FunctionClauseError) no function clause matching in Greeter2.hello/1

    The following arguments were given to Greeter2.hello/1:

        # 1
        %{age: "95", favorite_color: "Taupe"}

    iex:15: Greeter2.hello/1

入ってきたデータに対して独立してパターンマッチして、関数の中でそれらを使用できるようにしたことで、Elixirは複数の奥行きでパターンマッチするという点を確認しました。

リストの中で%{name: person_name}とpersonの順序を入れ替えたとしても、それぞれがfredとマッチングするので同じ結果となります。

つまり関数渡す側からは順番を気にする必要がないと

変数とマップを入れ替えてみましょう:

defmodule Greeter3 do
  def hello(person = %{name: person_name}) do
    IO.puts "Hello, " <> person_name
    IO.inspect person
  end
end

Greeter2.hello/1で使用した同じデータで実行してみます:

# call with same old Fred
...> Greeter3.hello(fred)
"Hello, Fred"
%{age: "95", favorite_color: "Taupe", name: "Fred"}

%{name: person_name} = person}%{name: person_name}がpersonに対してパターンマッチしているように見えたとしても、実際には それぞれが 渡された引数をパターンマッチしているということを覚えておいてください。

覚えておきます

まとめてくれてありがとうございます↓

まとめ: 関数は渡されたデータをそれぞれの引数で独立してパターンマッチします。 関数の中で別々の変数に格納するためにこれを利用できます。

プライベート関数

他のモジュールから特定の関数へアクセスさせたくない時には関数をプライベートにすることができます。 プライベート関数はそのモジュール自身の内部からのみ呼び出すことが出来ます。 Elixirではdefpを用いて定義することができます:

defmodule Greeter do
  def hello(name), do: phrase() <> name
  defp phrase, do: "Hello, "
end

iex> Greeter.hello("Sean")
"Hello, Sean"

iex> Greeter.phrase
** (UndefinedFunctionError) function Greeter.phrase/0 is undefined or private
    Greeter.phrase()

defpですね!!おぼえましたし

ガード

制御構造レッスンでもガードについて少しだけ触れましたが、これを名前付き関数に適用する方法を見ていきます。 Elixirはある関数にマッチするとそのガードを全てテストします。

以下の例では同じ名前を持つ2つの関数があります。ガードを頼りにして、引数の型に基づいてどちらを使うべきか決定します:

defmodule Greeter do
  def hello(names) when is_list(names) do
    names
    |> Enum.join(", ")
    |> hello
  end

  def hello(name) when is_binary(name) do
    phrase() <> name
  end

  defp phrase, do: "Hello, "
end

iex> Greeter.hello ["Sean", "Steve"]
"Hello, Sean, Steve"

これだと関数はオーバーロードされない感じなんですかね

型が指定されているから別の関数として扱われてるってことなの...?

ちょっと調べたらガード節が書いてあると同名関数でも別の処理をされるっぽいですね

デフォルト引数

引数にデフォルト値が欲しい場合、引数 \ デフォルト値の記法を用います

バックスラッシュふたつ

イコール置きたいところですがそれやるとさっきのパターンマッチングの例の通り動作して正常に動かないんですかね?

間違えそう

defmodule Greeter do
  def hello(name, language_code \\ "en") do
    phrase(language_code) <> name
  end

  defp phrase("en"), do: "Hello, "
  defp phrase("es"), do: "Hola, "
end

iex> Greeter.hello("Sean", "en")
"Hello, Sean"

iex> Greeter.hello("Sean")
"Hello, Sean"

iex> Greeter.hello("Sean", "es")
"Hola, Sean"

この例もしれっとパターンマッチングして呼び出す関数を分けてますね〜(phrase/1)

慣れないと結構気持ち悪い

先ほどのガードの例をデフォルト引数と組み合わせると、問題にぶつかります。 どんな風になるか見てみましょう:

defmodule Greeter do
  def hello(names, language_code \\ "en") when is_list(names) do
    names
    |> Enum.join(", ")
    |> hello(language_code)
  end

  def hello(name, language_code \\ "en") when is_binary(name) do
    phrase(language_code) <> name
  end

  defp phrase("en"), do: "Hello, "
  defp phrase("es"), do: "Hola, "
end

** (CompileError) iex:31: definitions with multiple clauses and default values require a header.
Instead of:

    def foo(:first_clause, b \\ :default) do ... end
    def foo(:second_clause, b) do ... end

one should write:

    def foo(a, b \\ :default)
    def foo(:first_clause, b) do ... end
    def foo(:second_clause, b) do ... end

def hello/2 has multiple clauses and defines defaults in one or more clauses
    iex:31: (module)

Elixirは複数のマッチング関数にデフォルト引数があるのを好みません。混乱の元になる可能性があります。 これに対処するには、デフォルト引数付きの関数を先頭に追加します:

好まないからコンパイルエラーだぜ!!!

同名関数にそれぞれ同一のデフォルト引数があるのは気持ち悪いからやめてクレメンスってことなんすね

defmodule Greeter do
  def hello(names, language_code \\ "en")

  def hello(names, language_code) when is_list(names) do
    names
    |> Enum.join(", ")
    |> hello(language_code)
  end

  def hello(name, language_code) when is_binary(name) do
    phrase(language_code) <> name
  end

  defp phrase("en"), do: "Hello, "
  defp phrase("es"), do: "Hola, "
end

iex> Greeter.hello ["Sean", "Steve"]
"Hello, Sean, Steve"

iex> Greeter.hello ["Sean", "Steve"], "es"
"Hola, Sean, Steve"

書き直す例もあってありがたい(なんでこれでいけるのかアレだけどそういうものっていう認識をした)

パイプライン演算子

パイプライン演算子(|>)はある式の結果を別の式に1つ目の引数として渡します。

まってましたパイプライン演算子

導入

プログラミングは厄介になりえます。実際とても厄介なことに、関数呼び出しを深くしすぎると把握するのがとても難しくなります。以下のネストされた関数を考えてみてください:

foo(bar(baz(new_function(other_function()))))

これ!!!!講義でLispなどの関数言語触ったときにカッコ閉じを数えるのつらすぎて無理無理の無理になってた記憶 しかない

ここでは、other_function/0の値をnew_function/1に、new_function/1の値をbaz/1に、baz/1の値をbar/1に、そして最後にbar/1の結果をfoo/1に渡しています。 Elixirではパイプライン演算子を使うことによって構文的な混沌に対し現実的にアプローチします。 パイプライン演算子(|>)は 一つの式の結果を取り、それを渡します。先ほどのコードスニペットをパイプライン演算子で書き直すとどうなるか、見てみましょう。

other_function() |> new_function() |> baz() |> bar() |> foo()

パイプライン演算子は結果を左に取りそれを右側に渡します。

みやすい!優勝!!

この例のセットのためにElixirのStringモジュールを使います。

おおまかに文字列をトークン化する

iex> "Elixir rocks" |> String.split()
["Elixir", "rocks"]

全てのトークンを大文字にする

iex> "Elixir rocks" |> String.upcase() |> String.split()
["ELIXIR", "ROCKS"]

文字列の終わりを調べる

iex> "elixir" |> String.ends_with?("ixir")
true

ベストプラクティス

関数のアリティが1より多いなら括弧を使うようにしてください。括弧の有無はElixirにおいてはたいした問題ではありませんが、あなたのコードを誤解するかもしれない他のプログラマにとっては問題です。もし3つ目の例でString.ends_with?/2から括弧を削除すると, 以下のように警告されます。

iex> "elixir" |> String.ends_with? "ixir"
warning: parentheses are required when piping into a function call. For example:

  foo 1 |> bar 2 |> baz 3

is ambiguous and should be written as

  foo(1) |> bar(2) |> baz(3)

true

関数のカッコの有無ってわりと可読性的な意味で扱い難しい...

まとめ

  • 無名関数fn(a, b) -> a + b end
  • 無名関数省略記法 &(&1 + &2)
  • パターンマッチングによる関数の分岐がしゅごい
  • アリティ(引数の数)が違うと同名でも別の関数として扱われる
  • 関数の引数はパターンマッチングが用いられいる
  • プライベート関数はdefp
  • ガードを用いることで引数の型に基づいて使う関数を決められる def hoge(names) when is_list(names) do...
  • デフォルト引数は\\、ただしガードと組合わせるときは注意
  • 関数はネストしないでパイプライン演算子(|>)を使おう

なかなかElixirが便利な理由が見えてきましたね〜〜

関数型言語のコードってなかなか馴染みがなくて難しいです