2022年5月7日 アルゴ式 番外編
プログラム上で入力を扱う
Ruby
N, V = gets.split(' ').map!(&:to_i) A = gets.split(' ').map!(&:to_i) puts N puts V puts A.class
JavaScript
function main(input) { const args = input.split("\n"); const inputArray = args.map((n) => n.split(" ")); const array1 = inputArray[0].map((n) => parseInt(n, 10)); const N = array1[0]; const V = array1[1]; const A = args[1].split(" ").map((n) => parseInt(n, 10)); console.log(N); console.log(V); console.log(A); } main(require('fs').readFileSync('/dev/stdin', 'utf8'));
- String.prototype.split()は、引数に渡した改行コードや空白文字にマッチする部分を取り除いて、その部分をコンマで要素を区切り配列を返します。
- parseInt()は整数の文字列をNumber型に変更します。第二引数は10進数を指定しています。
今回の問題
配列の全探索 2
Ryuji's answer
function main(input) { const args = input.split("\n"); const inputArray = args.map((n) => n.split(" ")); const array1 = inputArray[0].map((n) => parseInt(n, 10)); const N = array1[0]; const V = array1[1]; const A = inputArray[1].map((n) => parseInt(n, 10)); const results = A.filter(word => word === V); console.log(results.length); } main(require('fs').readFileSync('/dev/stdin', 'utf8'));
typescript
import * as fs from 'fs' const input = fs.readFileSync("/dev/stdin", "utf8") const [nv, alist] = input.split("\n") const [n,v,..._] = nv.split(" ").map(Number) const list = alist.split(" ").map(Number) const results: number[] = list.filter(word => word === v); console.log(results.length);
- 次回から関数で書いていきます!
Yano's answer
N, V = gets.split(' ').map!(&:to_i) A = gets.split(' ').map!(&:to_i) count_v = A.select { |n| n == V }.size puts count_v
- rubyではfilterとselectは同じ結果が得られます。
function main(input) { const args = input.split("\n"); const inputArray = args.map((n) => n.split(" ")); const array1 = inputArray[0].map((n) => parseInt(n, 10)); const N = array1[0]; const V = array1[1]; const A = args[1].split(" ").map((n) => parseInt(n, 10)); countV = A.filter(n => n === V).length console.log(countV); } main(require('fs').readFileSync('/dev/stdin', 'utf8'));
- countVの定義時にconstをつけ忘れていましたが出力に問題はありませんでした。
- 変数宣言が行われていない変数に値を格納した場合は、グローバルスコープの変数として宣言したとみなされます。strictモードではエラーになります。
Yuki's answer
import * as fs from 'fs' const input = fs.readFileSync("/dev/stdin", "utf8") const [nv, alist] = input.split("\n") const [n,v,..._] = nv.split(" ").map(Number) const list = alist.split(" ").map(Number) type CountSameNum = { (targetNum: number, array: number[]): number; }; const countSameNum: CountSameNum = (targetNum, array) => { const resultArray = array.filter((num) => num === targetNum); return resultArray.length; }; console.log(countSameNum(v, list));
◆ 型エイリアスで呼び出し可能オブジェクトを定義する
省略なし
type Fn = { (num1: number, num2: number): number }
省略記法
type Fn = (num1: number, num2: number) => number
参考
2022年5月6日 りあクト! 第7章 Reactをめぐるフロントエンドの歴史 (p.86~)
フロントエンドの歴史
年 | 出来事 |
---|---|
2005 | Googleマップが登場 |
2006 | jQueryが登場 |
2008 | GoogleがChromeのJavaScriptエンジンであるV8をオープンソース化して公開 |
2009 | V8エンジンを採用したJavaScriptの実行環境であるNode.jsがリリース |
2009 | ES5を発表 |
2010 | Backbone.js、Knockout、AngularJSなどのフレームワークが登場(後のVue.jsにも影響を与えたKnockoutの影響でJQueryへの依存が断ち切られる) |
2011 | Flash Playerの開発が中止される |
2013 | Reactが公開 |
2021 | HTML5が廃止され、HTML Living Standardが標準になる |
Reactを読み解く6つのキーワード
- Declarative (宣言的)
- Component-Based (コンポーネントベース)
- Just The UI (UIにしか関知しない)
- Virtual DOM (仮想DOM)
- One-Way Dataflow (単方向データフロー)
- Learn Once, Write Anywhere (ひとたび習得すれば、あらゆるプラットフォームで開発できる)
Reactの宣言的とは (Declarative)
宣言的プログラミングとは
出力の性質やあるべき状態を記述してプログラムを構成する事です。
命令型プログラミングとは
最終的な出力を得るために、時系列に沿って直前の状態に依存しながら命令を順番に書いていく手法の事です。
Reactの特徴の一つに、宣言的が掲げられています。 Reactは宣言的というコンセプトを2つの系統から得ました。
- Web Components → JSXにおいて、React Elementsを記述するスタイル
- 関数型プログラミング指向 → 関数コンポーネント、Hooks
どんなデータを表示されるべきかを記述しておくことでReactがそのデータを表示して適切なタイミングで表示を更新します。 (UIを宣言的に表現する)
参照透過性とは
参照透過性とは、同じ入力に対しては必ず同じ出力が得られることです。参照透過性が担保された関数は、同じ引数に対して、必ず同じ戻り値が返ってきます。
参考
2022年5月5日 Railsガイド Active Record の関連付け
has_many :through 関連付け
Physician・・・・医者
Appointment・・・診察予約
Patient・・・患者
class Physician < ApplicationRecord has_many :appointments has_many :patients, through: :appointments end class Appointment < ApplicationRecord belongs_to :physician belongs_to :patient end class Patient < ApplicationRecord has_many :appointments has_many :physicians, through: :appointments end
- 医者は、複数の診察予約を持つことができ、複数の患者を担当することが出来ます。
- 患者は、複数の診療予約を持つことができ、複数の医者から担当されることが出来ます。
- 診療予約にはどの医者とどの患者が関連付けされているかを、2つの外部キーで判断することが出来ます。
- throughオプションで2つの外部キーを持つ診療予約を参照することできます。
- physician.patientsで医者に診療予約した患者のリストを出すことが出来ます。
- patient.physiciansで患者が担当される医者のリストを出すことが出来ます。
中間テーブルの存在意義
usersとcourcesテーブルがあるとして、多対多のアソシエーションを直接紐付けると、1カラムの中にデータは1つずつしか入れられないため、1人のuserがいくつのcourceを選択するかわからないため、選択をしなかった場合に空のカラムができてしまい良くないテーブル設計になってしまいます。
ここで中間テーブルでそれぞれのuser_idとcources_idという外部キーを持たせたテーブルを用意する事で、空のカラムを作る事なく多対多のアソシエーションを組むことができます。
中間テーブルを使わない場合のデメリット
カラム数が多くなる
未使用(NULL)のカラムが多くなる
NULLが沢山入ってしまうアンチパターンという良くない設計になる
中間テーブルとは?
"join table もしくは junction table"とも呼ばれ、その名の通り、2つのテーブルの中間にもう一つ、それぞれに接続されたテーブルを指します。
中間テーブルには、接続先の外部キー同士を紐付けて格納しています。
例) twitterの良いね機能を考えます。 この場合、良いねする人(userテーブル)と良いねされる投稿(boardテーブル)があります。1人のユーザーは、複数の投稿に良いねでき、1つの投稿に対して複数のユーザーから良いねをもらうことができます(多対多)。このような場合に中間テーブルを使うことで良いねしたユーザーのuser_idと良いねされた投稿のboard_idをそれぞれ紐づけて格納してくれます。中間テーブルを使う場合、throughを使います。
class User < ApplicationRecord has_many :boards has_many :favorites # 中間テーブルと紐づけるための記述 has_many :favorite_boards, through: :favorites, source: :board end # いいねする人といいねした投稿を紐付ける中間テーブル # 外部キーであるuser_idとboard_idを格納しています。 class Favorite < ApplicationRecord belongs_to :user belongs_to :board end class Board < ApplicationRecord has_many :users has_many :favorites # 中間テーブルと紐づけるための記述 has_many :favorite_users, through: :favorites, source: :user end
user.favorite_boards
でユーザーが良いねした投稿の一覧を取得します。board.favorite_users
で投稿に良いねしたユーザーの一覧を取得します。
参考
2022年5月3日 りあクト! 第6章 stylelint (p.74~)
stylelintとは
stylelintはCSSのlinterです。
使用するには4つのパッケージをインストールします。
- stylelint ... stylelint本体です。
- stylelint-config-standard ... stylelint公式による標準の共有設定です。
- stylelint-order ... stylelintの並び順に関するルールセットのプラグインです。
- style-config-recess-order ... RECESSに基づくCSSの並び替えのための共有設定です。
yarn add -D stylelint stylelint-config-standard stylelint-order stylelint-config-recess-order (typesync) ← 型定義ファイルの読み込みはpackage.jsonのpreinstallに設定してあるのでインストール前に自動で走ります。
yarn ← yarn installの省略
stylelint導入後の設定
プロジェクトルートに.stylelintrc.jsを配置し、設定を追記します。
.stylelintrc.js
module.exports = { // extendsに共有設定を書く extends: [ 'stylelint-config-standard', // リセスはTwitterが提供していたCSSのコード品質ツール // リセスをstylelint-orderに移植したものが以下の設定。 'stylelint-config-recess-order' ], // pluginsにプラグインルールを書く plugins: [ 'stylelint-order' ], ignoreFiles: [ // node_modulsディレクトリ配下にあるCSSを対象外する。 '**/node_modules/**' ], // rulesはリンターが何を探し、何を訴えるかを決定します。 // Stylelint には 170 以上のルールが組み込まれています。 // デフォルトではどのルールもオンになっておらず、 // デフォルト値もありません。各ルールを有効にするには、明示的に設定する必要があります。 // キーはルールの名前、値はルールの設定です。 rules: { 'string-quotes': 'single' } };
- stylelint-config-standardは公式が提供している標準の共有設定です。
- 今回採用しませんでしたがstylelint-config-recommendedの方が広く使用されています。
- style-config-standardは、stylelint-config-recommendedを拡張しつつGoogle や Airbnb が定めた CSS スタイルガイドを適用した厳格な設定です。
VSCodeのsettings.jsonに設定する
設定を追加します。
{ + "css.validate": false, + "less.validate": false, + "scss.validate": false, "editor.codeActionsOnSave": { "source.fixAll.eslint": true, + "source.fixAll.stylelint": true }, "editor.formatOnSave": false, ︙
package.jsonにnpm scriptsの登録をする
ESlintやPrettierとまとめて走るようにします。
"scripts": { ... "lint": "npm run -s lint:style; npm run -s lint:es", "lint:fix": "npm run -s lint:style:fix && npm run -s lint:es:fix", "lint:es": "eslint 'src/**/*.{js,jsx,ts,tsx}'", "lint:es:fix": "eslint --fix 'src/**/*.{js,jsx,ts,tsx}'", "lint:conflict": "eslint-config-prettier 'src/**/*.{js,jsx,ts,tsx}'", "lint:style": "stylelint 'src/**/*.{css,less,sass,scss}'", "lint:style:fix": "stylelint --fix 'src/**/*.{css,less,sass,scss}'", "preinstall": "typesync || :" },
- ESLintは"lint:es"というオプション名に変更し、stylelintは"stylelint:es"としてオプションを追加します。
- 元々"lint"として設定していたESLintのオプションはlint:esとstylelint:esオプションをまとめて実行出来るように変更します。
--fix
ESLint や stylelint を --fix モードで走らせると、簡単なルール違反は自動で修正してくれますが、直しきれない複雑なものは手動で修正する必要があります。
Git Hooks
特定のアクションが発生したときに任意のスクリプトを走らせるGitのしくみです。 Git Hooksを管理するsimple-Git-hooksというツールがあります。 huskyというツールの方が有名です。しかし、5系以降に複雑な設定が増えた為、現在はsimple-Git-hooksも使われています。
上記のツールを用いてGitのコミットやプッシュ前後にLintのスクリプトを実行する設定をすることが出来ます。
参考
2022年5月2日 Rails開発者が採用面接で聞かれる想定Q&A 53問(翻訳)を深ぼる(Q1〜Q10)
基本的な用語
コレクション
コレクションとは,いくつかのオブジェクトをまとめて取り扱うための「容器」として振る舞うオブジェクトです。Rubyの標準ライブラリはいくつかのコレクション・クラスを提供しています。代表的なものはArray(配列)とHash(ハッシュまたは連想配列)です。 引用
Rubyにおけるオブジェクト
オブジェクト指向言語におけるオブジェクトとは、クラスのインスタンスのことです。 全てのインスタンス(オブジェクト)を作るクラスはclassクラスを継承しています。 class自体がclassクラスのインスタンスです。
irb(main):002:0> Class.class => Class
オブジェクト指向言語
オブジェクトを基本的な単位としてプログラムをつくることが出来る言語です。 オブジェクト指向とはそのプログラミングの性質のことをいいます。
オブジェクト指向言語ではオブジェクトの雛形であるクラス(class)やプロトタイプ(prototype)、および内部のメソッド(method)やプロパティ(property)を定義する構文や、オブジェクトのインスタンス化、メソッド呼び出しなどの機能が提供されています。
インスタンス変数
- インスタンス変数とは、同じインスタンス(同じオブジェクト)の内部で共有される変数です。
- インスタンス変数は、クラスの内部で使えます。
- インスタンス変数の変数名は必ず@で始めます。
- インスタンス変数はクラスの外部から参照できません。参照したい場合は、参照用のメソッドを作る必要があります。
class User def initialize(name) @name = name end # クラス内で定義したインスタンス変数は、 # クラス内で自由に使うことができます。 def hello puts "Hello, I am #{@name}" end end user = User.new('Alice') user.hello # => Hello, I am Alice
class User # インスタンス変数を参照するためには、ゲッターを定義する attr_reader :name def initialize(name) @name = name end def hello puts "Hello, I am #{@name}" end end user = User.new('Alice') user.hello # => Hello, I am Alice puts user.name # => Alice
ローカル変数
- ローカル変数とは、メソッドやブロックの内部で作成される変数です。
- @がついていない変数です。
- ブロックの外では使用出来ません。
sendメソッド
sendとは、レシーバの持っているメソッドを引数に渡すことで呼び出してくれるメソッドです。
obj.hello #=> 'hello' obj.send(:hello) #=> 'hello'
- objは何かのクラスのインスタンスで、そのクラス内でhelloメソッドが定義されているという前提です。
- objはhelloメソッドを持つため、sendを経由して実行することが出来ます。
デザインパターン
デザインパターンとは、「よく出会う問題とそれにうまく対処するための設計」をまとめたものです。
セッター・ゲッター
Rubyではゲッター(getter)を用いてインスタンス変数にアクセスでき、セッター(setter)を用いてインスタンス変数に値を設定できます。
セッター(クラス内)
def name_change=(name) @name = name end
クラス内で上記のようにセッターを定義することで値の変更を出来るようになります。 attr_writerやアクセサーを定義することで、上記の定義をせずにセッターが使用できます。
class User attr_reader :name attr_writer :name def initialize(name) @name = name end def hello puts "Hello, I am #{@name}" end end user = User.new('Alice') # インスタンスは、所属しているクラスに定義してあるメソッドを呼び出すことができる user.hello # => Hello, I am Alice puts user.name # => Alice user.name ="YUKI"; user.hello
ActiveRecordを継承したモデルのインスタンスの場合は、生成した段階でアクセサーが自動的に定義されるため、セッターやゲッターを自分で定義しなくても使用することが出来ます。
irb(main):001:0> @user = User.new() => #<User id: nil, email: nil, crypted_password: nil, salt: nil, created_at: nil, updated_at: nil, first_name: nil, last_name: nil, avatar: nil, reset_password_token: nil, reset_password_token_expires_at: nil, reset_password_email_sent_at: nil, access_count_to_reset_password_page: 0, role: "general"> # respond_to?を使うことで、オブジェクトがメソッドを持つかを判定できます。 irb(main):002:0> @user.respond_to?(:email) => true irb(main):003:0> @user.respond_to?(:email=) => true
参考
デザインパターンの基本 Rails開発者が採用面接で聞かれる想定Q&A 53問(翻訳) 7 Design Patterns to Refactor MVC Components in Rails
2022年4月30日 アルゴ式 番外編
アルゴ式とは
プログラミングを解くドリルが提供されているWEBサービスである。by yano
今回の問題
Ryuji's answer
function main(input) { const args = input.split("\n"); const inputArray = args.map((n) => n.split(" ")); const array1 = args[0].split(" ").map((n) => parseInt(n, 10)); const N = array1[0]; const V = array1[1]; const A = args[1].split(" ").map((n) => parseInt(n, 10)); const found = A.find(e => e === V); found ? console.log("Yes") : console.log("No"); } main(require('fs').readFileSync('/dev/stdin', 'utf8'));
- ArrayのメソッドをMDNに見にいきました。
- メソッドを閲覧している時にfindメソッドが目に止まりました。
- Node.jsでSequelizeのfindOneメソッドでログインの処理を実装したときにデータベースに登録してあるメールアドレスと入力したメールアドレスを照合する時のロジックが頭の中で浮かび、findメソッドでできそうだと仮説を立てて挙動を確認したら今回の問題に使えると思いました。
- Aの配列の中からVに一致する値をfound変数に代入するようにしました。
- 三項演算子を使い実行結果を条件分岐し提出したところLGTMでした。
Yano's answer
N, V = gets.split(' ').map!(&:to_i) A = gets.split(' ').map!(&:to_i) if A.include?(V) puts "Yes" else puts "No" end
pメソッドを使用するとテストが通らず不正解になるため、注意です。
N, V = gets.split(' ').map!(&:to_i) A = gets.split(' ').map!(&:to_i) puts A.include?(V) ? "Yes" : "No"
三項演算子パターンの場合、上記で正解となります。
map!(&:to_i)
以下とほぼ同義です。 map!とするとレシーバを破壊的に変更することができます。
.map {|n| n.to_i}
Yuki's answer
function main(input) { const args = input.split("\n"); const inputArray = args.map((n) => n.split(" ")); const array1 = args[0].split(" ").map((n) => parseInt(n, 10)); const N = array1[0]; const V = array1[1]; const A = args[1].split(" ").map((n) => parseInt(n, 10)); const hasArray = A.find((num) => num === V) ? "Yes" : "No"; console.log(hasArray); } main(require('fs').readFileSync('/dev/stdin', 'utf8'));
TypeScript
import * as fs from 'fs' const input = fs.readFileSync("/dev/stdin", "utf8") const [nv, alist] = input.split("\n") const [n,v,..._] = nv.split(" ").map(Number) const list = alist.split(" ").map(Number) const hasArray = list.find((num) => num === v) ? "Yes" : "No"; console.log(hasArray);
- 条件に一致する配列の要素が存在する場合、Yesを返しています。
Yui's answer
// 入力値を受け取る function main(input) { const args = input.split("\n"); // 入力文字列を改行で分割し、新しい配列を作成 const inputArray = args.map((n) => n.split(" ")); // さらに空白文字列で配列を分割 const array1 = args[0].split(" ").map((n) => parseInt(n, 10)); const N = array1[0]; const V = array1[1]; const A = args[1].split(" ").map((n) => parseInt(n, 10)); // 配列の要素の中に整数Vが含まれるか判定 if (A.find((element) => element === V)){ console.log("Yes"); } else { console.log("No"); } } main(require('fs').readFileSync('/dev/stdin', 'utf8'));
- readFileSyncメソッドは、同期的にファイルを読み込むfsモジュールのメソッドです。
fs.readFileSync(path\[, options\])
が基本形です。 - fsモジュールは、Node.jsでファイルの読み書きを行うための基本的な関数を提供するモジュールです。
参考
2022年4月29日 りあクト! 第6章 特別なフォーマッタ 『Prettier』 (p.67~)
Prettier
Prettierはフロントエンドのメジャーどころをほぼ網羅しているフォーマッタです。 TypeScript、HTML、CSS、Vue、Angular、GraphQL、CSS in JS の styled-components など
コードフォーマッタとは
インデントや改行などの記述スタイルのフォーマットを統一して整形するツールを指します。
導入するメリット
開発者がコードの書き方のスタイルをめぐる論争をやめさせる目的で開発されているので、新人からベテランまで一律のコードに直してくれるところです。(無駄な言い争いがなくなります) 具体的には以下になります。
- ソースコードの品質を保つため(コードのスタイルの一貫性を保つため)
- コードレビュー時に、設計や命名などの重要な箇所に集中するため(コードのスタイルの指摘に時間を割くのを防ぐため)
- 複数のメンバーが各自の整形ルールを適用し、更新する度に余計な差分が発生することを防ぐため
- ソースコードを綺麗にするための労力(スタイル定義の議論や時間)を費やさなくて済むため
- ツールに任せられることはツールに任せてしまった方が今後楽になるため
ESLintとPrettierを併用する理由
Prettier 入門 ~ESLintとの違いを理解して併用する~に、ESLintとPrettierを併用する理由が詳しく記載されているので、以下に引用します。
ESLint でもeslint --fixでコード整形ができるが、Prettier の方がコード整形が優れているから。
具体的には以下の点が優れている。
・ESLint では整形できないコードを整形できる
・ESLint と比べて手軽で確実に整形できる
但し、PrettierはESLintのような構文チェック機能がない
そのため、コードの整形は Prettier が行い、コードの構文チェックは ESLint が行うように併用する必要があります。
Prettierの環境を作る
パッケージの導入
ESLintの環境にPrettierを加えるには、以下の2つのパッケージを導入します。
- prettier
- Prettier本体
- eslint-config-prettier
- Prettierと競合する可能性のあるESLintの各種ルールを無効にする共有設定
パッケージをインストールしたら.eslintrc.jsのextendsオプションの最後に'prettier'を追加します。
.prettierrcでPrettierのオプションを設定
設定オプション一覧はドキュメントに記載があります。 https://prettier.io/docs/en/options.html
書籍では以下の3項目の設定を変更していました。
{ "singleQuote": true, "trailingComma": "all", "endOfLine": "auto" }
カスタマイズした設定以外はデフォルト値が適用されます。
ESlintで設定したルールとPrettierで設定したルールが衝突していないかをチェックする
$ npx exlint-config-prettier 'src/**/*.{js,jsx,ts,tsx}' No rules that are unnecessary or conflist with Prettier were found.
上記は衝突していない場合に表示されます。
衝突した場合
The following rules are unnecessary or might conflict with Prettier: - @typescript-eslint/indent
この場合は@typescript-eslint/indent
の設定が不要なので .eslintrc.js
から削除します。
Prettierがnpmのscriptsで実行されるようにpackage.jsonに登録する
"scripts": { ︙ "eject": "react-scripts eject", + "fix": "npm run -s format && npm run -s lint:fix", + " f o r m a t " : " p r e t t i e r - - w r i t e - - l o g l e v e l = w a r n '{public,src}/**/*.{js,jsx,ts,tsx,html,gql,graphql,json}'", "lint": "eslint 'src/**/*.{js,jsx,ts,tsx}'", "lint:fix": "eslint --fix 'src/**/*.{js,jsx,ts,tsx}'", + "lint:conflict": "eslint --print-config .eslintrc.js | eslint-config-prettier-check", "preinstall": "typesync" },
衝突ルールの検出もしています。
VSCodeの設定
settings.jsonに追記します。
"editor.defaultFormatter": "esbenp.prettier-vscode", "[graphql]": { "editor.formatOnSave": true }, "[javascript]": { "editor.formatOnSave": true }, "[javascriptreact]": { "editor.formatOnSave": true }, "[json]": { "editor.formatOnSave": true }, "[typescript]": { "editor.formatOnSave": true }, "[typescriptreact]": { "editor.formatOnSave": true },
VSCodeのデフォルトのフォーマッタにPrettierを設定して各種言語の保存時に自動整形できるようにしています。
保存時に自動整形できるようになりました。
メモ
パスを通すとは
コマンド検索パス(コマンドサーチパス) を追加することです。
パスを通す理由
コマンド名と同じファイル名を持つ実行ファイルを探索できるようにするためです。パスを通すことで、実行ファイルのパスを入力しなくても、コマンドで実行ファイルを実行できます。
source
パスの内容をその場で反映させるためのコマンドです。
echo
画面にメッセージを表示させるためのコマンドです。 「echo $変数名」など環境変数やシェル変数を表示する際にも使用します。