- 投稿日:2020-05-27T23:51:12+09:00
[Ruby]配列で要素を習得する際の添え字の役割
添え字について、読み流していたのでこの機会にまとめてみました。
配列における添え字の役割
添え字a = [1,2,3,4,5,6,7] a[2,5] #=> [3,4,5,6,7]引数のひとつ目が、位置
引数のふたつ目が、長さを表す。ちなみに
添え字の代入a = [1,2,3,4,5,6,7] a[2,5]= 777 a #=> [1,2,777]要素の3番目から、5つの要素一括で777に置き換わります。
values_atとlast
こっからは配列のおまけ。
values_ata = [1,2,3,4,5,6,7] a.values_at[2,5] #=> [3,6] a = [1,2,3,4,5,6,7] a.values_at[2,5,6] #=> [3,6,7]取得したい要素の数に応じて、添え字を追加できる。
lasta = [1,2,3,4,5,6,7] a.last #=> 7 a = [1,2,3,4,5,6,7] a.last(4) #=> [4,5,6,7]配列の最後の要素を取り出すメソッドlastは、引数に0以上の値を渡すと、
その分だけ要素を後ろから取り出します。
最初みたときは、引数に指定した値を後ろから数えて、要素を1つ取得するのかと思っていました。配列だけ見ても、新たな発見ばかりで面白いですね。
今日はこの辺で。
- 投稿日:2020-05-27T23:11:36+09:00
『オブジェクト指向設計実践ガイド』を読んでいく
決意表明
1か月ちょい~2か月かけて少しずつ読み進めたい。
要点
オブジェクト指向プログラミングの概要・導入
1. オブジェクト指向設計
1.1 設計の賞賛
- オブジェクト指向設計の手法に従えば、コーディングを楽しくし、かつ最も効率良くソフトウェアを生産できる!
- オブジェクト指向設計 = 依存関係を管理すること。
- 部品
- オブジェクト
- 相互作用
- メッセージ
- 設計の目的は変更コストの削減。
1.2 設計の道具
- 設計原則『SOLID』
- デザインパターン『GoF』
1.3 設計の行為
- BUFD(ウォーターフォール?)とオブジェクト指向設計とでは"設計"の意味が異なる。
- BUFD
- 未来の内部動作を特定した、完全な文書化。
- オブジェクト指向設計
- 変更を前提とした、小さな領域におけるコード構成。
1.4 オブジェクト指向プログラミングのかんたんな導入
- 手続き型言語
- 変数が持つデータ型は一つ。
- データと振る舞いは別物。
- オブジェクト指向言語
- オブジェクトは振る舞いを持つ。
- いくつもの型(=クラス)を持つ。
- オブジェクトの振る舞いを予想できる。
- オブジェクト指向言語は型を拡張できる。よって、オブジェクト指向アプリケーションはプログラマーの扱う特別独自のプログラミング言語となっていく。
クラスに焦点を当てる
2.単一責任のクラスを設計する
- アプリケーションのモデル化という目標には2つの異なる基準がある。
- クラスを使い、「いますぐに」求められる動作を行う。
- 「あとにも」かんたんに変更できるようにする。
- プログラミングの技術力が如実に現れる部分。
2.1 クラスに属するものを決める
- TRUEなコードを書くための第一歩は、単一の責任(=最小で有用)を持つよう徹底すること。
2.2 単一の責任を持つクラスを作る
- すべてのクラスやメソッドは、単一の責任を持つようにする。
- 単一責任とは?
- クラスの持つメソッドを質問に言い換えたときに、意味をなす質問になっている。
- 一文でクラスを説明できる。
- 「それと」や「または」が含まれない。
2.3 変更を歓迎するコードを書く
- インスタンス変数やデータ構造は隠蔽する = 振る舞い(メソッド)へと変更する。
- 予期せぬ変更がコードに影響を与えることを防ぐ。
- いったんすべてのメソッドを単一責任にしてしまうことで、クラスのスコープが明白になる。
3.依存関係を管理する
オブジェクト中心の設計からメッセージ中心の設計へ
4.柔軟なインターフェースをつくる
5.ダックタイピングでコストを削減する
6.継承によって振る舞いを獲得する
7.モジュールでロールの振る舞いを共有する
8.コンポジションでオブジェクトを組み合わせるテストの設計
9.費用対効果の高いテストを設計する
- 投稿日:2020-05-27T22:17:53+09:00
rails での初めてのページネーション機能の追加
rails での初めてのページネーション機能の追加
参考にした記事(https://qiita.com/residenti/items/1ae1e5ceb59c0729c0b9)
ページネーションとは
長くなってしまったコンテンツを複数のページに分割して情報を読み取ることです。
googleの検索結果などが例に挙げられます。まずgemのinstall
kaminariというgemを使って実装します。
gemfilegem'kaminari', '~> 0.17.0'rails5系ではkaminariのバージョンを上記のように指定しないと動作しないそうです。bundle installします。
コントローラー
topics_controller@topics=Topic.page(params[:page]).per(5)@topics=Topic.all
を上記のように変更しました。perメソッドで1ページに何件まで表示するか決定します。View
index.html.erb<% @topics.each do |a| %> 問題:<%=a.question %><br><br> 1:<%=a.choice1 %><br> 2:<%=a.choice2 %><br> 3:<%=a.choice3 %><br> 4:<%=a.choice4 %><br> <% end %> <%= paginate @topics %><%= paginate @topics %>を書くだけです。
以上でページネーション機能の追加が完了しました。
- 投稿日:2020-05-27T22:00:43+09:00
Ruby で始める GraphQL Client
これは何
- Ruby のコードから GraphQL API を叩く方法を紹介します
- Ruby のコードで GraphQL API を作る方法は紹介しません
GraphQL とは
GraphQL とは何か、を一言で表現するのは難しいのですが、個人的には「API を表現するプロトコルの一種」というような認識です。
REST API と比較されることが多いです。REST API でcurl http://example.com/users/123/posts/のようにして叩くところを、GraphQL では
curl http://example.com/graphql -X POST --data ' { user("123") { posts { title } } } 'のように叩きます。REST API では
/users/
/users/123/
/users/123/posts/
のように様々な URL にリクエストを投げて目的の情報を手に入れますが、GraphQL では 1 つのエンドポイントに様々なクエリを投げ分けることで、必要な情報を全て(不必要な情報は除いて)1 度のリクエストで取得することができます。GraphQL API の利用方法を学ぶ
取り急ぎ GraphQL API を利用することができればよかったので、私は GraphQL 公式ガイド (https://graphql.org/learn/) の
Queries and Mutations
とSchemas and Types
だけを読みました。やや長めのページ 2 つを読むだけで概要が理解できたのでおすすめです。ただし注意点が 1 つあります。 例として登場する API の意味を深く考えてはいけません。 例えば、冒頭でこんなクエリが登場します。
{ hero { name } }これに対して、サンプル API はこんなレスポンスを返しています。
{ "data": { "hero": { "name": "R2-D2" } } }この例を見たとき、「hero_id を指定したわけでもないのになぜ R2-D2 を返すんだろう?」「hero_id を指定しないときは hero 全てを返す方がいいのでは?」というのが気になって、全体の理解の妨げになってしまいました。実際にはこの API はただのサンプルでしかないので、実用性などは無視して「そういうもの」と思い込む必要があります。単に「任意引数として Episode を 1 つ受け取り、Character を 1 つ返す、hero という Query」があるだけです。
学習用 GraphQL サーバーを立てる
上述した GraphQL 公式ガイドに出てくる謎のスターウォーズ API を再現したものが apollographql/starwars-server です。このあと Ruby から叩くのに使うので、さくっと起動しておきましょう。ブラウザから http://localhost:8080/graphql にアクセスできれば起動成功です。
なお、似た名前のものに graphql/swapi-graphql がありますが、これはより本格的なスターウォーズ API の GraphQL ラッパーなので別物です。気をつけましょう。
Ruby で GraphQL クライアントを使ってみる
Ruby 製の GraphQL Client として github/graphql-client があります。github がオーナーという安心感が凄かったので、ひとまずこれを使おうと思います。
README にも書かれていますが、以下のようにして使います。
require "graphql/client" require "graphql/client/http" module SWAPI # http アダプターを設定 HTTP = GraphQL::Client::HTTP.new("http://localhost:8080/graphql") # 上記を使って API サーバーから GraphQL Schema 情報を取得 Schema = GraphQL::Client.load_schema(HTTP) # 上記を使ってクライアントを作成 Client = GraphQL::Client.new(schema: Schema, execute: HTTP) end HeroQuery = SWAPI::Client.parse <<~'GRAPHQL' { hero { name } } GRAPHQL result = SWAPI::Client.query(HeroQuery) pp result.to_h上記では Schema 情報をサーバーから取得していますが、以下のようにしてあらかじめローカルに保存しておき、それをロードする方法もあるそうです。
module SWAPI # http アダプターを設定(上例と同じ) HTTP = GraphQL::Client::HTTP.new("http://localhost:8080/graphql") # ローカルファイルから GraphQL Schema 情報を取得 Schema = GraphQL::Client.load_schema("path/to/schema.json") # 上記を使ってクライアントを作成(上例と同じ) Client = GraphQL::Client.new(schema: Schema, execute: HTTP) end # API サーバーから GraphQL Schema 情報を取得してローカルファイルに出力 GraphQL::Client.dump_schema(SWAPI::HTTP, "path/to/schema.json") # ...注意点
クエリに Operation Name を付ける場合、
SWAPI::Client.query()
の引数にモジュール名のように付けて渡す必要があります。HeroQuery = SWAPI::Client.parse <<~'GRAPHQL' # NameOfHero という名前をつけた場合・・・ query NameOfHero { hero { name } } GRAPHQL # HeroQuery ではなく HeroQuery::NameOfHero を渡す必要がある! result = SWAPI::Client.query(HeroQuery::NameOfHero)これを忘れると、
expected definition to be a GraphQL::Client::OperationDefinition but was GraphQL::Language::Nodes::Document (TypeError)
というエラーが起きます。またこのため、必然的に小文字から始まる名前も付けられません(
client.rb:241:in 'const_set': wrong constant name ... (NameError)
というエラーが起きます)。この ↓ あたりの issue は全て単にこの仕様を見落としている気がします。
- https://github.com/github/graphql-client/issues/212
- https://github.com/github/graphql-client/issues/171
- https://github.com/github/graphql-client/issues/148
README に明記されていないので私もハマってしまいました・・・(コントリビュートチャンス?)
- 投稿日:2020-05-27T21:41:48+09:00
Hello RSpec
新人のRailsエンジニアです。
RailsのTestをするにあたってRSpecを学んだのでまとめてみました。
100日後に1人前になるエンジニアを連載中です。本日は7日目
ちなみにMinitestとRSpecの比較についてはこちらRSpecとは
RubyやRuby on Railsで作ったクラスやメソッドをテストするためのドメイン特化言語 (DSL)を使ったフレームワーク。
つまりは、テスト専用のプログラム言語とも言えます。FactoryBot
テスト用のデータの作成をサポートするgem
FactoryBotを利用するとテスト用のデータを簡単に準備できる
Rails標準ではFixtureという仕組みもありますが、
こちらよりもデータの状態やデータ間の関係性を掴みやすい。
ということで今回はこちらも使用。Capybara
WebアプリケーションのE2E(End-to-End)テスト用フレームワーク。
RSpecなどと組み合わせて使います。Webアプリケーションのブラウザ操作をシュミレーションできる。RSpecの基本形
RSpecの記述の基本形としては以下の様な形です。
describe [仕様を記述する対象(テスト対象)], type[specの種類] do context[ある状況] do before do [事前準備] end it[仕様の内容(期待の概要)] do [期待する動作] end end endこれだけみてもなかなかわからないので分割してみていきます。
describe
describe [仕様を記述する対象(テスト対象)], type[specの種類] do enddescribeには、何について仕様を記述しようとしているのかを書きます。
例えばsystemテストをプロフィール表示機能について仕様を書きたいのであればdescribe 'プロフィール表示機能', type: :system doと言った具合ですね。
context
こちらはテストの内容を状況、状態のバリエーションごとに分類するために利用します。
例えばユーザーの入力内容が正しいか間違っているか、ユーザーがログインしているか否かなどを
contextに記述していきます。before
beforeはその領域全体の前提条件を実現するためのコードを記述している
以下のだとプロフィールを表示するための前提条件。
この場合だとログインしていることが前提条件になっているcontext 'ユーザーがログインしているとき' do before do visit login_path fill_in 'メールアドレス', with: 'email@example.com' fill_in 'パスワード', with: 'password' click_button 'ログイン' end endit
最後のitは期待する動作と文章をブロック内のコードで記述します
expect(page).to have_content 'hoge'
上の記述は画面に"hoge"という内容があるよね??
っていう内容になっています。
expect to have contentなので英語のままといえばそれまでですが。#プロフィール画面に'ユーザーのプロフィール'って内容があるよね?? it 'ユーザーのプロフィールが表示される' do expect(page).to have_content 'ユーザーのプロフィール' end以上まとめてみると
describe 'プロフィール表示機能', type: :system do context 'ユーザーがログインしているとき' do before do visit login_path fill_in 'メールアドレス', with: 'email@example.com' fill_in 'パスワード', with: 'password' click_button 'ログイン' end it 'ユーザーのプロフィールが表示される' do expect(page).to have_content 'ユーザーのプロフィール' end end endこう言った形でRSpecの基本形が成り立っています。
いきなり全部理解しようとするとつらいけど、分割してみるとなんとかなりそう。今回は基本形をどう見るかという点でまとめてみました。
実際はもっとネストがあって、込み入っている様に見えますが、
まずは基本形をおさえてRSpecの理解を深めていきたいですね。本日は以上です
1人前のエンジニアになるまであと93日
- 投稿日:2020-05-27T20:41:43+09:00
Active Record、複雑なクエリ
Active Recordでちょっと複雑な問い合わせを書いた時に、忘れないようにメモとして追記していきます。
関連テーブルのレコード数でソート
class PLine < ApplicationRecord has_many :likes, dependent: :destroy end class Like < ApplicationRecord belongs_to :p_line end@p_lines = PLine.select('p_lines.*', 'count(likes.id) AS favs') .left_joins(:likes) .group('p_lines.id') .order('favs desc')<% @p_lines.each do |p_line| %> <p><%= p_line.p_line %></p> <p><%= p_line.favs %> points</p> <% end %>関連カラムが複数あって、変数の値がどちらかに一致する。関連カラムをすべてincludeしたい。
class Mc < ApplicationRecord has_many :videos end class Video < ApplicationRecord belongs_to :mc1, class_name: 'Mc' belongs_to :mc2, class_name: 'Mc' end@videos = Video.includes(:mc1, :mc2).where(mc1_id: @mc) .or(Video.includes(:mc1, :mc2).where(mc2_id: @mc))
- 投稿日:2020-05-27T20:24:11+09:00
Ruby : Nokogiriが自分でエンコーディングを参照しに行く場所を調べてみた
はじめに
こちらの記事で、Nokogiriはエンコーディング指定が
nil
だったときに、パース元のhtmlのmeta要素のcharsetを見に行くと結論づけました。
今回は結論が本当にあっているか確かめるために、公式ドキュメントを追ってみた話です。公式ドキュメントを追ってみる
Nokogiri公式ドキュメント
今回はこちらの公式ドキュメントを追っていきます。もちろん英語です。
普段自分は英語の公式ドキュメントは避けがちなのですが、決心して見に行ってみます。英語は読めなくてもコードなら読めます。多分。Nokogiri::HTML::Documentクラス
普通Nokogiriを使ってパースするときには
Nokogiri::HTML.parse(html)
のように書いているのですが、正式にはNokogiri::HTML::Document
クラスのようです。
Documentクラス欄を開いて.parse
メソッドを探し、「view source」でソースを表示してみます。以下ソース
lib/nokogiri/html/document.rbdef parse string_or_io, url = nil, encoding = nil, options = XML::ParseOptions::DEFAULT_HTML options = Nokogiri::XML::ParseOptions.new(options) if Integer === options # Give the options to the user yield options if block_given? if string_or_io.respond_to?(:encoding) unless string_or_io.encoding.name == "ASCII-8BIT" encoding ||= string_or_io.encoding.name end end if string_or_io.respond_to?(:read) url ||= string_or_io.respond_to?(:path) ? string_or_io.path : nil unless encoding # Libxml2's parser has poor support for encoding # detection. First, it does not recognize the HTML5 # style meta charset declaration. Secondly, even if it # successfully detects an encoding hint, it does not # re-decode or re-parse the preceding part which may be # garbled. # # EncodingReader aims to perform advanced encoding # detection beyond what Libxml2 does, and to emulate # rewinding of a stream and make Libxml2 redo parsing # from the start when an encoding hint is found. string_or_io = EncodingReader.new(string_or_io) begin return read_io(string_or_io, url, encoding, options.to_i) rescue EncodingFound => e encoding = e.found_encoding end end return read_io(string_or_io, url, encoding, options.to_i) end # read_memory pukes on empty docs if string_or_io.nil? or string_or_io.empty? return encoding ? new.tap { |i| i.encoding = encoding } : new end encoding ||= EncodingReader.detect_encoding(string_or_io) read_memory(string_or_io, url, encoding, options.to_i) endまずここに注目してみます
lib/nokogiri/html/document.rbif string_or_io.respond_to?(:encoding) unless string_or_io.encoding.name == "ASCII-8BIT" encoding ||= string_or_io.encoding.name end end
string_or_io
は普段自分がhtmlを指定している変数です。
解釈して見るに、string_or_io
がencoding
メソッドを持っており、そのエンコーディング名がASCII-8BIT
でなく、encoding
引数が定義されていなかったら、encoding
はstring_or_io
のエンコーディング名とする、のようです。なるほど!だからhtmlをバイナリモードで開かなかった場合には、htmlの開き方(エンコーディング)に依存してしまうので、パース後に文字化けが発生してしまうことがあるということだったんですね!
ではファイルをバイナリモードで開き、かつ
encoding
引数がnil
のときにはどうなるのか。
今度はここに注目してみます。lib/nokogiri/html/document.rbencoding ||= EncodingReader.detect_encoding(string_or_io)
encoding
引数が定義されていなかったら、EncodingReader.detect_encoding
メソッドを使うとあります。
おとなしくドキュメントのEncodingReader.detect_encoding
メソッドを見に行きます。先ほどと同じようにソースを表示します。
以下ソースlib/nokogiri/html/document.rbdef self.detect_encoding(chunk) if Nokogiri.jruby? && EncodingReader.is_jruby_without_fix? return EncodingReader.detect_encoding_for_jruby_without_fix(chunk) end m = chunk.match(/\A(<\?xml[ \t\r\n]+[^>]*>)/) and return Nokogiri.XML(m[1]).encoding if Nokogiri.jruby? m = chunk.match(/(<meta\s)(.*)(charset\s*=\s*([\w-]+))(.*)/i) and return m[4] catch(:encoding_found) { Nokogiri::HTML::SAX::Parser.new(JumpSAXHandler.new(:encoding_found)).parse(chunk) nil } else handler = SAXHandler.new parser = Nokogiri::HTML::SAX::PushParser.new(handler) parser << chunk rescue Nokogiri::SyntaxError handler.encoding end endメソッド引数の
chunk
に、今回はstring_or_io
、つまり普段自分がhtmlとしているものが入ります。見慣れないメソッドが多いのでぱっと正確な意味がとれないですが、2番目のifブロックにmetaのcharsetを参照しているような記述がありませんか???
returnで値を返しているようですし、この箇所が非常に怪しい感じがします。さいごに
まだソースの細かい箇所が把握できていませんが、自分の求める答えにぐっと近づけた気がします。
ソースの詳細がわかれば、また別記事でまとめようと思います。
- 投稿日:2020-05-27T19:01:12+09:00
"ExecJS::RuntimeError"
起動はするのに、、、
railsにて、初めの段階でハマったのでメモメモ。
エラー内容
ExecJS::RuntimeError
バージョン
Rails 5.2.4
ruby 2.5.1解決策
Gemfile
gem 'mini_racer', platforms: :ruby
上記のコメントアウトを外してあげて、ターミナルで[bundle install]してあげれば、完了。
参考URL
ExecJS::RuntimeError(2019年版)
https://github.com/railsgirls-jp/coach.info/issues/27
- 投稿日:2020-05-27T16:21:10+09:00
【Ruby on Rails+devise+sendgrid】Signupでのメール送信機能を使う手順解説
環境
ruby 2.6.4
Rails 5.2.4.2
rbenv 1.1.2
mysql2 0.5.3
やりたいこと
コピペのみでメール送信機能を実装できる手順を紹介します
元ネタは
【Rails5.2/sendgrid】超簡単!Signupのメール送信を本番環境で使うための手順(Heroku)Part1ですsendgridを使って簡単に実装していきます
sendgrid導入手順
1. Herokuにプロジェクトを作成
$ heroku create2. プロジェクト名を変更
$ heroku rename 変更後のプロジェクト名3. プッシュ
$ git push heroku master4. sendgridコマンドを通す
$ heroku addons:create sendgrid:starter5. sendgridページからAPIキーを作成&コピー
6. APIキーを設定
$ heroku config:set SENDGRID_USERNAME=apikey $ heroku config:set SENDGRID_PASSWORD=取得したAPIキー7. environment.rbを修正
config/environment.rb# Load the Rails application. require_relative 'application' # Initialize the Rails application. Rails.application.initialize! ActionMailer::Base.smtp_settings = { :address => 'smtp.sendgrid.net', :port => '587', :authentication => :plain, :user_name => ENV['SENDGRID_USERNAME'], :password => ENV['SENDGRID_PASSWORD'], :domain => 'heroku.com', :enable_starttls_auto => true }8. development.rbに下の2行を追加
config/environments/development.rbconfig.action_mailer.delivery_method = :test config.action_mailer.default_url_options = { :host => 'http://previewurlforyourapp'}9. rails s
$ rails s10. application.html.erbを修正
app/views/layouts/application.html.erb<div class="navbar navbar-default navbar-static-top"> <div class="container"> <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-responsive-collapse"> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a class="navbar-brand" href="#">PhotoApp</a> <div class="navbar-collapse collapse navbar-responsive-collapse"> <ul class="nav navbar-nav"> <li><%= link_to "Link1", "/path1" %></li> <li><%= link_to "Link2", "/path2" %></li> <li><%= link_to "Link3", "/path3" %></li> </ul> <ul class="nav navbar-right col-md-4"> <% if current_user %> <li class="col-md-8 user-name"> <%= link_to ('<i class="fa fa-user"></i> ' + truncate(current_user.email, length: 25)).html_safe, edit_user_registration_path, title: 'Edit Profile' %> </li> <li class="col-md-1"> </li> <li class="col-md-3 logout"><%= link_to('Logout', destroy_user_session_path, class: 'btn btn-xs btn-danger', title: 'Logout', :method => :delete) %></li> <% else %> <li class="col-md-4 pull-right"> <%= link_to('Sign In', new_user_session_path, class: 'btn btn-primary', title: 'Sign In') %> </li> <% end %> </ul> </div> </div> </div>11. app/assets/stylesheets/custom.css.scssを作成&修正
app/assets/stylesheets/custom.css.scss.user-name { padding: 0 !important ; padding-left: 5px; padding-top: 15px !important; text-align: center; a { color: black !important; margin: 0 !important; padding: 5px!important; } a:hover, a:focus { color: #000 !important; } } .logout { padding-left: 0; } .nav.navbar-right { padding-bottom: 10px; padding-top: 5px; } .nav.navbar-nav { .navbar-link { border-radius: 5px; color: #fff; margin-top: 15px; padding: 8px; } .navbar-link:focus, .navbar-link:hover { background: #3071a9; color: #fff; } li a { margin-right: 5px; } } .nav.navbar-right li { .btn { color: #fff !important; margin-top: 5%; } .btn-danger:hover, .btn-danger:focus { background-color: darken(#d9534f,20%) !important; } .btn-primary:hover, .btn-primary:focus { background-color: darken(#428bca,20%) !important; } } .btn-primary:visited, .btn-danger:visited { color: #fff; }参考文献
2つともRails初心者にとても詳しく書かれているので一度目を通しておくことをおすすめします
特に本番環境でsendgridを使いたい方はとても参考になります【Rails5.2/sendgrid】超簡単!Signupのメール送信を本番環境で使うための手順(Heroku)Part1
https://www.twinzlabo.com/rails_sendgrid_easy/
【Rails5.2/sendgrid】超簡単!Signupのメール送信を本番環境で使うための手順(Heroku)Part2
https://www.twinzlabo.com/rails_sendgrid_easy_2/
- 投稿日:2020-05-27T15:32:23+09:00
Ruby と Python で解く AtCoder ABC130 D 累積和 二分探索
はじめに
AtCoder Problems の Recommendation を利用して、過去の問題を解いています。
AtCoder さん、AtCoder Problems さん、ありがとうございます。今回のお題
AtCoder Beginner Contest D - Enough Array
Difficulty: 859今回のテーマ、累積和 + 二分探索
問題文
(条件)連続部分列に含まれる全ての要素の値の和は、K 以上である。
より累積和を使用することが分かりますが、1≦N≦100_000
ですので2重にループを回すとTLE
になります。
そこで、累積和は単調増加ですので、計算量を抑えるために二分探索も併用します。Ruby
ruby.rbn, k = gets.split.map(&:to_i) a = gets.split.map(&:to_i) acc = 0 x = [0] + a.map{|v| acc += v} cnt = 0 x.each do |y| j = x.bsearch_index{|z| z - y >= k} if j == nil break else cnt += n - j + 1 end end puts cntaccumulate.rbacc = 0 x = [0] + a.map{|v| acc += v}累積和はここで計算しています。
追記
コメントでいただいたコードに修正しました。bsearch.rbj = x.bsearch_index{|z| z - y >= k}二分探索ですが、C++ でいうところの
lower_bound
が、Ruby ではbsearch
若しくはbsearch_index
になります。Python
python.pyfrom sys import stdin def main(): import bisect import itertools input = stdin.readline n, k = map(int, input().split()) a = list(map(int, input().split())) x = [0] + list(itertools.accumulate(a)) cnt = 0 for z in x: j = bisect.bisect_left(x, k + z) if j <= n: cnt += n - j + 1 print(cnt) main()accumulate.pyx = [0] + list(itertools.accumulate(a))Python では、累積和を求める
accumulate
という関数があります。bisect.pyj = bisect.bisect_left(x, k + z)二分探索ですが、
bisect
若しくはbisect_left
bisect_right
を使用します。
探索する数値が配列に含まれる場合、bisect
はその数値の右側、bisect_left
は左側の位置を返します。for.pyfor z in x: for i in range(n):
range()
でループを回すとTLE
しました。
Ruby Python コード長 (Byte) 238 377 実行時間 (ms) 165 101 メモリ (KB) 11780 14196 まとめ
- ABC 130 D を解いた
- Ruby に詳しくなった
- Python に詳しくなった
参照したサイト
instance method Array#bsearch
すごいぞitertoolsくん
bisect --- 配列二分法アルゴリズム
- 投稿日:2020-05-27T15:03:50+09:00
% rails db:createするとmimemagic に起因するLoadErrorががが
% rails db:create rails aborted! LoadError: cannot load such file -- mimemagic /projects/〜〜〜/config/application.rb:7:in `<main>' /projects/〜〜〜/Rakefile:4:in `<main>' bin/rails:4:in `<main>'git hubからとあるファイルをcloneしdb:createをした際に生じたエラー
まいむまじっく.......???
なかなかマイナーなファイルが原因を引き起こしてるっぽいけども・・・ひとりでは解決困難!!先輩の力を借りてる最中、Qiitaをみてみるとこんな記事が
a.com/zQmjRAb73seN5RM/items/5fa74d2a4d346cdd386e
バージョンの問題っぽい・・・?
その通りにコマンドを実行してみた$gem list 〇〇 //〇〇には該当ファイルを入れる(今回だとmimemagic)
結果は......
mimemagic (default: 0.3.5, 0.3.4, 0.3.3)
ではどんなファイルがあるのかを確認してみる
find ~/.rbenv -type f | grep 〇〇 //〇〇には該当ファイルを入れる(今回だとmimemagic)
結果は......
(一部抜粋) /.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/mimemagic-0.3.3/test/files/image.png /.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/mimemagic-0.3.3/test/files/application.zip /.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/mimemagic-0.3.3/CHANGELOG.md /.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/mimemagic-0.3.3/script/freedesktop.org.xml /.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/mimemagic-0.3.3/script/generate-mime.rb /.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/mimemagic-0.3.3/README.md /.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/mimemagic-0.3.3/Rakefile /.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/mimemagic-0.3.3/mimemagic.gemspec /.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/mimemagic-0.3.3/.gitignore /.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/mimemagic-0.3.3/lib/mimemagic/tables.rb /.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/mimemagic-0.3.3/lib/mimemagic/version.rb /.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/mimemagic-0.3.3/lib/mimemagic/overlay.rb /.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/mimemagic-0.3.3/lib/mimemagic.rb /.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/mimemagic-0.3.3/Gemfile /.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/mimemagic-0.3.3/.travis.yml /.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/mimemagic-0.3.4/.yardopts /.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/mimemagic-0.3.4/LICENSE /.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/mimemagic-0.3.4/test/mimemagic_test.rb /.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/mimemagic-0.3.4/test/files/application.vnd.openxmlformats-officedocument.spreadsheetml{gdocs}.sheet /.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/mimemagic-0.3.4/test/files/application.x-tar /.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/mimemagic-0.3.4/test/files/application.gzip /.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/mimemagic-0.3.4/test/files/application.x-bzip /.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/mimemagic-0.3.4/test/files/application.vnd.openxmlformats-officedocument.spreadsheetml{msoffice}.sheet /.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/mimemagic-0.3.4/test/files/image.jpeg /.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/mimemagic-0.3.4/test/files/application.x-ruby /.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/mimemagic-0.3.4/test/files/image.png /.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/mimemagic-0.3.4/test/files/application.vnd.openxmlformats-officedocument.spreadsheetml{rubyxl}.sheet /.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/mimemagic-0.3.4/test/files/application.zip /.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/mimemagic-0.3.4/CHANGELOG.md /.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/mimemagic-0.3.4/script/freedesktop.org.xml /.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/mimemagic-0.3.4/script/generate-mime.rb/Users/tech-camp/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/mimemagic-0.3.3/test/files/image.png /.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/mimemagic-0.3.3/test/files/application.zip /.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/mimemagic-0.3.3/CHANGELOG.md/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/mimemagic-0.3.3/script/freedesktop.org.xml /.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/mimemagic-0.3.3/script/generate-mime.rb /.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/mimemagic-0.3.3/README.md /.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/mimemagic-0.3.3/Rakefile /.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/mimemagic-0.3.3/mimemagic.gemspec /.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/mimemagic-0.3.3/.gitignore /.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/mimemagic-0.3.3/lib/mimemagic/tables.rb /.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/mimemagic-0.3.3/lib/mimemagic/version.rb /.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/mimemagic-0.3.3/lib/mimemagic/overlay.rb /.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/mimemagic-0.3.3/lib/mimemagic.rb /.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/mimemagic-0.3.3/Gemfile /.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/mimemagic-0.3.3/.travis.yml /.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/mimemagic-0.3.4/.yardopts /.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/mimemagic-0.3.4/LICENSE /.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/mimemagic-0.3.4/test/mimemagic_test.rb
みてみると 0.3.3 0.3.4のファイルはあることになっているが、0.3.5がないとのこと
ここが食い違っているから生じたエラー is 濃厚......
この食い違いを解決するには、% gem install mimemagic -v 0.3.5
結果
Successfully installed mimemagic-0.3.5
そして畳みかけます
% bundle install % rails db:create
成功!!!!!!!
LoadErrorのさいはぜひ参考にしてみてください!
- 投稿日:2020-05-27T14:55:31+09:00
RSpec RequestSpecでsessionを利用する
Rspec request specでセッションの読み込み、書き込みを実現してみる
今回はActionDispatch::Requestのsessionメソッドを上書きする形で対応します
allow_any_instance_of(ActionDispatch::Request).to receive(:session).and_return({})事前にセッションを書き込む方法
rails_helper.rb
に上記のsessionメソッド上書きを実装しますrails_helper.rbRSpec.configure do |config| # ... # テストケース共通の事前処理 config.before(:each) do # let(:rspec_session) で指定された値を セッションの初期値とします session = defined?(rspec_session) ? rspec_session : {} # destroyメソッドを実行してもエラーにならないようにします(必要であれば) session.class_eval { def destroy; nil; end } # sessionメソッドを上書き allow_any_instance_of(ActionDispatch::Request).to receive(:session).and_return(session) end end以下のように テストコード内の変数でセッションの初期値を設定できます
let(:rspec_session) { { user_id: 1 } }事前にセッションを書き込む&実行後のセッションを取得する方法
config.add_setting()
を使用し、session変数をグローバルで扱えるようにします上記の
rails_helper.rb
に更に追記しますrails_helper.rbRSpec.configure do |config| # ... # テストケース共通の事前処理 config.before(:each) do # let(:rspec_session) で指定された値を セッションの初期値とします session = defined?(rspec_session) ? rspec_session : {} # destroyメソッドを実行してもエラーにならないようにします(必要であれば) session.class_eval { def destroy; nil; end } # 追記 実行後のセッションを取得できるようにする config.add_setting(:session, :default => session) # 変更 sessionメソッドをRSpec.configuration.sessionで上書き allow_any_instance_of(ActionDispatch::Request).to receive(:session).and_return(RSpec.configuration.session) end endテスト実施後に
RSpec.configuration.session
を取得することで、実行後のセッションを確認できますexpect(RSpec.configuration.session[:user_id]).to eq 1参考にしました
Method: RSpec::Core::Configuration#add_setting
いいね!と思ったら LGTMお願いします?
- 投稿日:2020-05-27T14:00:58+09:00
railsでGoogleMapAPIの導入
このページのコードでできること
・自分のrailsプロジェクトにGoogleMapを埋め込み、表示させる。
・ページを開いたら地図と、初期値のマーカーを表示させる。
・inputボックスと検索ボタンを用意。
・inputボックスに検索したい場所を入力し、検索ボタンを押すことでその場所の地図を表示。Maps JavaScript API & Geocoding APIの取得
これら記事にお世話になりました。(参考文献より)
コードを書く前に
Google Maps API を使ってみた
Google MAP 名称から場所を検索・特定する
オリジナルアプリ作成 〜RailsでGoogleMap利用検証〜コード
postsに記述するとします。
@post.locationは一例です。ご自身のアプリケーションにふさわしい初期値を入れてください。posts/index.html.erb<div id='target'></div> <div class='map-btn'> <input id="address" type="textbox" value="<%= @post.location %>"> <input type="button" value="検索" onclick="codeAddress()"> <div> <script src="https://maps.googleapis.com/maps/api/js?key=自分のAPIキー&callback=initMap" async defer></script>style.scss#target { height: 300px; width: 300px; }Javascriptの記述
post.jslet map let geocoder let centerp = {lat: 33.60639, lng: 130.41806} function initMap(){ geocoder = new google.maps.Geocoder() map = new google.maps.Map(document.getElementById('target'), { center: centerp, zoom: 12, }); marker = new google.maps.Marker({ position: centerp, map: map }); } function codeAddress(){ let inputAddress = document.getElementById('address').value; geocoder.geocode( { 'address': inputAddress}, function(results, status) { if (status == 'OK') { map.setCenter(results[0].geometry.location); var marker = new google.maps.Marker({ map: map, position: results[0].geometry.location }); } else { alert('該当する結果がありませんでした:' + status); } }); }参考文献
大変お世話になりました。感謝。
コードを書く前に
[https://qiita.com/nagaseToya/items/e49977efb686ed05eadb]Google Maps API を使ってみた
[https://qiita.com/Haruka-Ogawa/items/997401a2edcd20e61037]Google MAP 名称から場所を検索・特定する
[https://qiita.com/yoshi_yast/items/521c1f36306a180f45dd]オリジナルアプリ作成 〜RailsでGoogleMap利用検証〜
[https://note.com/daddy0055/n/nddbe8da38bbc]
- 投稿日:2020-05-27T11:20:30+09:00
MySQLのモードに関して
自分のローカルで設定されているモード
$ mysql --version mysql Ver 14.14 Distrib 5.7.29, for osx10.15 (x86_64) using EditLine wrapper $ mysql -uroot -p mysql> select @@global.sql_mode; +------------------------------------------------------------------------------------------------------------------------------------------------------+ | @@global.sql_mode | +------------------------------------------------------------------------------------------------------------------------------------------------------+ | STRICT_TRANS_TABLES,STRICT_ALL_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,TRADITIONAL,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION | +------------------------------------------------------------------------------------------------------------------------------------------------------+ 1 row in set (0.00 sec)
- STRICT_TRANS_TABLES
- STRICT_ALL_TABLES
- NO_ZERO_IN_DATE
- NO_ZERO_DATE
- ERROR_FOR_DIVISION_BY_ZERO
- NO_AUTO_CREATE_USER
- NO_ENGINE_SUBSTITUTION
なお、これは紛れもなく、TRADITIONALモードである。TRADITIONALモードとは上の7つのモードが全て有効になっている状態を言う組み合わせモードなのだ。
STRICTモード
STRICT_TRANS_TABLESモードとSTRICT_ALL_TABLESモードのいずれかが有効な場合、それはSTRICTモードである。
トランザクションストレージエンジン
- 不正な値が挿入されようとした場合、エラーを発し(不良データ値は調整されない)、ロールバックする
非トランザクションストレージエンジン
- STRICT_TRANS_TABLES
- 1行目で不良データを挿入しようとした時はエラーを起こし終了
- 2行目以降で不良データを挿入しようとした時は不良データを調整して挿入し、(エラーではなく)警告を発する
- STRICT_ALL_TABLES
- 1行目でも2行目以降で不良データを挿入しようとした時はエラーを起こし終了(部分更新が行われる)
参考
というかもはや以下の要約
MySQLのSQLモードをstrictモードで設定する。
- 投稿日:2020-05-27T10:37:28+09:00
【Ruby】僕がしていたモジュールにしていた勘違い【初学者】
Rubyの勉強中の僕がしていたモジュールに関する勘違いを勝手に紹介します。
⬇勘違いしていたときのコード⬇
⬇正解コード(間違いがあればご指摘いただけると幸いです)⬇
違いとしてはモジュールの記述されている「thinkable.rb」に
require "./human"
があるかないかの違いです。
実際、あってもなくても実行結果に変化はありませんがこの記述は不要なものなので無いほうが良いでしょう。そもそもなぜこのような勘違いをしていたのか
なぜ僕がこのような勘違いをしていたのかと言うとモジュールというものをしっかり理解できていなかったということがありますがモジュール内で「human.rb」で定義されている「self.hobby」を利用するのだから「human.rb」を「tahinkable.rb」で読み込まなきゃいけないんじゃね。というように思っていたからです。
require "./human"
の記述があってもなくても結果は変わらなかったので私が受講中のTeacAcadmyでメンターの方に聞いてみると親切に回答していただけました。答えとしては
module は Mix-in して初めて動作する
「self.hobby」も読み込まれた先の「self.hobby」を参照するための記述のため、「./human」を読み込んでいなくても、「Human」に読み込んだ時に動作するというものでした。
つまりモジュールはクラスで読み込まれた時に初めて動作するということだったんですね。一つ勘違いをなくすことができました。
以上、初学者の私がしていた勘違いを勝手に紹介させていただきました。
- 投稿日:2020-05-27T10:09:41+09:00
こんなソースコードはイヤだ-クォーテーションはシングルなのダブルなの?
- 投稿日:2020-05-27T08:48:33+09:00
ruby subメソッドで使用した正規表現について
address="都道府県,市区町村,番地,建物名部屋番号"
という変数があり、ここから、番地を取得したいとします。
結論からいうと、address.split(",")[2]
ですが、
正規表現でチャレンジしました。まずはsubで、戦闘の「都道府県,」を消して、それを2回繰り返そうと思いました。
のこった「番地,建物名部屋番号」から、「,」以降を消して終わりです。まずは1回だけのバージョンでテスト
address.sub(/.+,/,'')
「都道府県,」のみが消える予定です。結果
"建物名部屋番号"
となっちゃいました。/.+,/の部分が「都道府県,」になるだろうと予測したのですが、
「都道府県,市区町村,番地,」になっちゃいます。試しに
address.sub(/,/,'')
としてみると
「都道府県市区町村,番地,建物名部屋番号」
となり、最初の1個目をちゃんとけしてくれます。なぜに???
・・・・・
まぁ番地のみ取得という問題は解決しているので
そういうもんだろうと今は割り切って次に進みます。
まだまだ、勉強中なもので、、、
今後勉強していけば、この疑問も晴れるかも、、、
よかったら誰か教えてTT
- 投稿日:2020-05-27T07:18:53+09:00
Rubyの文字列とシンボルの違い【初学者】
初投稿です。
誤りがあればご指摘お願いします。
文字列とシンボルの違い
文字列とシンボルは見た目は似ていますが別物。
どう違うかと言うと与えられるオブジェクトIDが違います。
文字列の場合、同じ文字列を2つ用意した場合でも与えられるオブジェクトIDは異なるものになりますが、シンボルの場合、同じ文字列を2つ用意した場合でも同じオブジェクトIDが与えられます。
具体的に見てみるとこんな感じです。
実行結果
オブジェクトに対してオブジェクトIDを表示してくれるメソッドの「object_id」を利用してオブジェクトIDを表示するとこのようになります。上2つの文字列("JUN")に対するオブジェクトIDはそれぞれ違ったものが表示されていますが、シンボル(:JUN)に対するオブジェクトIDは同じものが表示されています。
ちなみに
当然のことですがもう一度実行すると1回めと比べて文字列に対して与えられるオブジェクトIDは異なりますが、シンボルのオブジェクトIDは1回目と同じものです。さらに1回目の実行時はすべて大文字(:JUN)で表示しましたが小文字で(:jun)で表記した場合は大文字のときとは違ったオブジェクトIDが与えられます。
- 投稿日:2020-05-27T00:42:36+09:00
【Ruby/Rails】破壊的メソッドを利用した時にNoMethodErrorが出た時に対処したこと
起きたエラー
filter!の類の破壊的メソッドを使った時にNoMethodErrorが出ました。
対処出来たのでメモがてら残します。(間違ったら優しく教えてください…。)
再現する方法
object_controller.rbdef index @object = Object.all @object.filter! do |o| o.id != 1 end end※命名適当なのは勘弁してください。
オブジェクトをallとかで配列で取得した時に、filterみたいな非破壊的メソッドは使えたのですが、filter!みたいな破壊的メソッドで条件に一致しないオブジェクトを弾いた配列を作ろうとしてました。
ですが、一番最初の見出しの通りNoMethodErrorを吐きました。
対処した方法
object_controller.rbdef index @object = Object.all @object.to_a.filter! do |o| o.id != 1 end end
to_a
でオブジェクトを配列に変換するとNoMethodErrorを吐かなくなりました。理由とかは調べて見つけたら追記します。
一旦、忘れないようにメモがてらに残します。(念押し)
既に理由とか知っている人がいて、それくらい当然っしょみたいなノリでしたら、何かにリンクをペタッと貼ってもらえる紳士な方がいてくれると最高に嬉しいです…。