フロントエンドあれこれ

昨今のフロントエンド事情について行けていないので、アプリケーション作成時によく聞く単語などを整理する。
間違っているところもあるかもしれないが、現時点での理解を残す。

Webpack

複数のファイルを1つにまとめる。らしい。

ESLint

JavaScript用の静的解析ツール。スタイルの統一や、明らかなバグ(Null参照とか?)の発見に使用する。

babel

JavaScriptのコードを新しい書き方から古い書き方へと変換するツール。

yarn

JavaScriptで使用するパッケージ管理システムの1つ。
npm(後述)と互換性がある。

npm

yarnと同様に、パッケージ管理のためのツール。

npx

npmに同梱されているコマンド。 よくわからんが、npmパッケージを簡単に実行できるコマンド、らしい。

node(Node.js)

サーバ側でプログラムを動作させることができるJavaScript実行環境の1つ。

終わりに

とりあえずあれだな、これらのツールを使用して何らかのJavaScriptのアプリでも作成してみるのが一番いいかもしれない。

3連休中の読書

以下の3冊を読みました。 ざっくり内容や感想を書き記します。

証言 羽生世代

証言 羽生世代 (講談社現代新書)

証言 羽生世代 (講談社現代新書)

将棋界のレジェンド羽生善治と同世代の棋士を総称して「羽生世代」と呼ぶのですが、その羽生世代および上の世代と下の世代からのインタビューをまとめた本です。 「なぜ羽生世代はこんなにも強いのか」「なぜ羽生世代という集団が生まれたのか」など、将棋ファンにとって非常に読み応えのある1冊です。

ブラック・ショーマンと名もなき町の殺人

ブラック・ショーマンと名もなき町の殺人

ブラック・ショーマンと名もなき町の殺人

  • 作者:東野 圭吾
  • 発売日: 2020/11/30
  • メディア: 単行本(ソフトカバー)
ガリレオ」シリーズなどでおなじみの東野圭吾さんの新刊です。
今回の主役は元マジシャンのちょっと変わった人物。警察を相手に手八丁口八丁で情報を仕入れつつ、独自に事件を解決していくというストーリーになっています。
元マジシャンですがなぜか警察の捜査内容だったりに詳しく、徐々に真相に近づいていく話は引き込まれます。
現在のコロナ下の状況もうまくストーリーに反映していて、時世をうまく取り入れたなーという印象です。

元彼の遺言状

このミステリーがすごい!」で大賞を受賞した小説です。 主人公は金にがめつい、性格がちょっときつめの女性弁護士。
元カレが死際に残した遺言状の内容を巡って奮闘するストーリーです。
主人公の性格がちょっときついものの、(冒頭で安物の婚約指輪にキレる)所々で温かみのある言動もあり、なかなかに魅力的な人物。

2つのArrayからMapを生成する

結論

ziptoMapを使用することで簡単にできます。

    val names = arrayOf("one", "two", "three")
    val nums = arrayOf(1, 2, 3)

    val nameAndNums = names.zip(nums).toMap()
    println(nameAndNums) // {one=1, two=2, three=3}

zipは、List<Pair<T, R>を返却するメソッドです。 toMapは名前の通り、Mapを生成するためのメソッドです。
Iterable<Pair<K, T>>の拡張関数として定義されています。

気になったこと

用意した配列のサイズが異なっていたらどうなるんだ?と思い、実行してみると、少ない方のサイズに合わせるように動くことがわかりました。

    val names = arrayOf("one", "two", "three", "four")
    val nums = arrayOf(1, 2, 3, 4, 5)

    val nameAndNums = names.zip(nums)
    println(nameAndNums)
    // [(one, 1), (two, 2), (three, 3), (four, 4)]
    // 5はペアとして生成されない

結果としては直感的というか、まぁそうですよね、という感想ですね。

Converter<S, T>を使用してリクエストパラメータを任意の型に変換する

Springのコントローラが受け取る引数は基本的にあらかじめ決められた型のものしか受け付けません。 (Stringなど)

そのため、パラメータでオリジナルの型として受け取りたい場合はConverter<K, T>を使用するといいでしょう。

例えば以下のようなコントローラのメソッドと型UserIdがあるとします。 この場合、リクエストクエリに単純にパラメータを渡すだけではUserIdに変換することができず、エラーとなってしまいます。

    @GetMapping("demo")
    fun demo(
        @RequestParam userId: UserId
    ): ResponseEntity<String> {
        return ResponseEntity.ok("ok")
    }

data class UserId(
    val value: UUID
)

この時、Springで用意されているConverter<S, T>を使用することで問題を解決することができます。
以下のようなクラスを作成します。

@Component
class UserIdConverter : Converter<String, UserId> {
    override fun convert(source: String): UserId {
        return UserId(UUID.fromString(source))
    }
}

上記のようにコンポーネントを作成することで、コントローラに渡された文字列をUserId型に自動的に変換します。

JavaScriptでNullを上手に扱う

近年、Null安全という言葉と共に、各言語でNullを安全に扱うための構文が一般的になってきています。
こちらの記事にあるように、この流れはJavaScriptに限った話ではありません。

今回はJavaScriptに限った話になりますが、Nullを扱う際に便利な構文をいくつか紹介できればと思います。

論理和 (||)

論理和を使用すると、変数のデフォルト値を定義する際に便利です。これはJavaScriptの特性によるもので、JavaScriptではnullfalseとして判定する言語仕様となっています。(null以外にも数値の0なども同様です。)
そのため、以下のコードを実行すると、hoge || "default"は、変数hogefalseとして扱われることにより論理和の右辺が評価され"default"objに代入されることとなります。

let hoge = null
let obj = hoge || "default";
console.log(obj) //default

Optional Chaining (?.)

次に、Optional chainingを見ていきます。
こちらは、変数がnullの場合にundefinedを、そうでなければチェインした式を実行することになります。例を見てみましょう。

var user1 = {
    name: "hoge",
    age: 30,
    address: {
        country: "japan",
        city: "tokyo"   
    }
}

var user2 = {
    name: "hoge",
    age: null,
    address: null
}

console.log(user1?.name) //hoge
console.log(user1.address?.country) //japan
console.log(user2.address?.country) //undefined

この構文を使用すると、万が一変数がnullであったとしても実行時エラーとなりません。例えばuser2.address.countryとして呼び出すと、TypeError: Cannot read property 'country' of nullが発生してしまいます。
とはいえ、undefinedが返されるというのも実用的ではありませんので、Optional Chainingは後述するNull合体と一緒に使うと良いでしょう。

Null合体 (??)

というわけでNull合体を見ていきましょう。
Null合体演算子??を使用すると、左辺の値がnullまたはundefinedの時、右辺を返し、それ以外の場合は左辺の値を返します。

var user2 = {
    name: "hoge",
    age: null,
    address: null
}
console.log(user2?.name) //hoge
console.log(user2.address?.country ?? "unknown") //unkown

Null合体とoptional chainingを組み合わせることで安全な変数の取り扱いができるようになります。

Null合体代入 (??=)

最後に、Null合体代入を見てみましょう。 Null合体代入を使用すると、変数がnullまたはundefinedの場合にのみ値の代入ができるようになります。
例を見てみましょう。

let a = null
a ??= 100

let b = 50
b ??= 100

console.log(a) //100 aはnullだったため、代入が行われる
console.log(b) //50 bはnullでなかったため、代入が行われない

以上、JavaScriptnullを安全に使う方法をいくつか紹介しました。 ただ、これから何かJavaScriptで作ろうとしているのなら、TypeScriptを使えばよりnullに対して安全になるので、そちらも考慮しても良いかもしれません。

それではまた。

ABC179 復習

A - Plural Form

末尾の文字に従って分岐をします。

s = input()

if s[-1] == 's':
  print(s + 'es')
else:
  print(s + 's')

B - Go to Jail

タプルなどで同時に投げたサイコロの目を保持しておきます。 3回連続の判定は変数なり、現在のindexから2つ先までも同じ目になっているかを見ます。

n = int(input())
d = [tuple(map(int, input().split())) for i in range(n)]

for i in range(n - 2):
  if d[i][0] == d[i][1]:
    if d[i + 1][0] == d[i + 1][1] and d[i + 2][0] == d[i + 2][1]:
      print('Yes')
      break
else:
  print('No')

C - A x B + C

単純に解くとO(N3)になってしまうため、少し工夫が必要になります。 結論から言ってしまうと、BとCは考慮する必要がなく、Aのみ1~Nまで調べれば良いです。よって計算量はO(N)となり、十分間に合います。

n = int(input())

ans = 0

for A in range(1, n):
  #Cが0では条件が成り立たないため、割り切れる場合は1つ前まで
  if n % A == 0:
    ans += n // A - 1
  else:
    ans += n // A

print(ans)

D - Leaping Tak

DPと累積和の合わせ技です。

n, k = map(int, input().split())

l = []
r = []
for i in range(k):
  a, b = map(int, input().split())
  l.append(a)
  r.append(b)

mod = 998244353

dp = [0] * (n + 1)
#1マス目の行き方は1通り
dp[1] = 1
#累積和を保持する配列
dpsum = [0] * (n + 1)
dpsum[1] = 1
#2マス目〜Nマス目まで、遷移できる数を更新する
for i in range(2, n + 1):
  for j in range(k):
    li = i - r[j]
    ri = i - l[j]
    if ri < 0:
      continue
    li = max(1, li)

    dp[i] += (dpsum[ri] - dpsum[li - 1]) % mod
  
  dpsum[i] = dpsum[i - 1] + dp[i]

print(dp[n] % mod)

終わりに

今回の結果でまたレーティングが下がってしまいました。 D問題は解く方向としては間違っていなかったので、また精進あるのみですね。 とりあえずA〜Cはミスなしで行かないとレーティング下がり続けそうです・・。