パンうめぇ

園児ニアの日記帳

Rails APIを高速に立ち上げる

ハッカソンで高速にAPIサーバーを立ち上げるためのメモです.

Rails API 立ち上げ

1 プロジェクト作成

$ rails new rails_api --api --skip-bundle
$ bundle install

2 create db

$ rails generate scaffold User name:string
$ rails db:create
$ rails db:migrate

3 routing設定

  • route.rb
Rails.application.routes.draw do
  # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
  scope :api, defaults: { format: :json } do
    resources :users
  end
end

4 access! localhost:3000/api/users

post

% curl -X POST -H "Content-Type: application/json" -d '{"name": "hoge"}' http://localhost:3000/api/users

get

% curl http://localhost:3000/api/users

Heroku Deploy

1 heroku create heat-monitor

2 sqlite3development, testに移動し,productionpgを追加. herokuはpostgresqlを使うので, production環境ではpostgresqlに変更する.

group :production do
  gem 'pg'
end

roup :development, :test do
  # Call 'byebug' anywhere in the code to stop execution and get a debugger console
  gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
  gem 'sqlite3', '~> 1.4'
end

3 bundle install % bundle install --without production

4 database.ymlを変更

production:
  <<: *default
#  database: db/production.sqlite3
  adapter: postgresql
  encoding: unicode
  pool: 5

5 コミット git add -A & git commit -m "first commit"

6 herokuにpush git push heroku master

7 heroku環境でmigrate heroku run rails db:migrate

8 access!(4とほぼ同じ)

参考サイト

M-SOLUTIONS プロコンオープン 2020 C 累積和で解く

M-SOLUTIONS社のプロコンに参加しました.

C問題で累積積を書いて提出したのですが桁が大きすぎてWA. 解説をみたところ対数変換で累積和に直せば同じようなロジックで解けるとのことで実装して提出しました.

atcoder.jp

考え方

logで対数変換し,累積和を取りiからi-kの値を引くことでそれぞれの学期評価が求まります. あとはそれを比較し答えを求めます.

c++にはlog10という便利な関数があり,簡単に対数変換できることがわかりました. また対数変換の際には誤差に対応する必要があるため少数型にします.

f:id:kanekou:20200731132308p:plain
4学期の評価を求める場合(i=4, k=3)

解いたコード

#include <algorithm>
#include <cmath>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <utility>
#include <vector>
#define rep(i, n) for (int i = 0, i##_len = (n); i < i##_len; ++i)
#define reps(i, n) for (int i = 1, i##_len = (n); i <= i##_len; ++i)
#define rrep(i, n) for (int i = ((int)(n)-1); i >= 0; --i)
#define rreps(i, n) for (int i = ((int)(n)); i > 0; --i)
#define INF 1000000000000
#define MOD 1000000007

 
int main() {
    ll n, k;
    cin >> n >> k;
    vector<long double> a(n + 1, 0);
    vector<long double> csum(n + 1, 0);
    reps(i, n) cin >> a[i];
    reps(i, n) csum[i] = log10(a[i]) + csum[i - 1];
 
    long double pre = csum[k];
    for (int i = k + 1; i <= n; i++) {
        long double result = csum[i] - csum[i - k];
        if (pre < result)
            cout << "Yes" << endl;
        else
            cout << "No" << endl;
        pre = result;
    }
    return 0;
}

ほんとはA[i] < A[i-k]を比較するだけで良いのですが,気付きませんでした... 算数強くなりたい.

DFS

競プロをしているのですが,DFSにまだ慣れないので,備忘録として問題と考え方を残してきたいと思います.

ABC114 C

atcoder.jp

7, 5, 3の組み合わせの数を求める問題です.

考え方としては,以下のように3, 5, 7それぞれを全探索し,[3,5,7]を組み合わせてできるn以下の数字をカウントすれば良いです.

f:id:kanekou:20200725143035p:plain
それぞれの木において[3,5,7]の組み合わせでできる数をカウントする.

解いたコード

#include <algorithm>
#include <cmath>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <utility>
#include <vector>
#define rep(i, n) for (int i = 0, i##_len = (n); i < i##_len; ++i)
#define reps(i, n) for (int i = 1, i##_len = (n); i <= i##_len; ++i)
#define rrep(i, n) for (int i = ((int)(n)-1); i >= 0; --i)
#define rreps(i, n) for (int i = ((int)(n)); i > 0; --i)
#define INF 1000000000000
#define MOD 1000000007

using namespace std;
using ll = long long;
vector<ll> ans;
ll n;

void dfs(ll p) {
    if (p > n) {
        return;
    }
    if (to_string(p).find("3") != string::npos &&
        to_string(p).find("5") != string::npos &&
        to_string(p).find("7") != string::npos) {
        ans.push_back(p);
    }
    dfs(p * 10 + 3);
    dfs(p * 10 + 5);
    dfs(p * 10 + 7);
}

int main() {
    cin >> n;
    dfs(3);
    dfs(5);
    dfs(7);

    cout << ans.size() << endl;
    return 0;
}

わざわざvector<ll> anspush_backしたのはデバックのためです.普通にカウントすればいいです.

ATC 001

atcoder.jp

迷路を探索してゴールを目指す基本問題です. 解き方として,4方向でDFSしながら記録を取り,すでに探索した所や壁,枠外にきた際に戻る感じです.

座標がy,xなところに少し注意しなければいけないかも.

解いたコード

#include <algorithm>
#include <cmath>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <utility>
#include <vector>
#define rep(i, n) for (int i = 0, i##_len = (n); i < i##_len; ++i)
#define reps(i, n) for (int i = 1, i##_len = (n); i <= i##_len; ++i)
#define rrep(i, n) for (int i = ((int)(n)-1); i >= 0; --i)
#define rreps(i, n) for (int i = ((int)(n)); i > 0; --i)
#define INF 1000000000000
#define MOD 1000000007

using namespace std;
using ll = long long;
int h, w;
vector<string> field;
bool seen[500][500] = {{false}};

void dfs(int y, int x) {
    if (x < 0 || y < 0 || x >= w || y >= h) return;  //out
    if (field[y][x] == '#') return;  // wall
    if (seen[y][x]) return;          // already seen
    seen[y][x] = true;

    // 4 directions
    dfs(y + 1, x);  // ↑
    dfs(y, x + 1);  // →
    dfs(y - 1, x);  // ↓
    dfs(y, x - 1);  // ←
}

int main() {
    cin >> h >> w;
    field.resize(h);
    rep(i, h) cin >> field[i];

    // find start and end position
    int start_x, start_y, end_x, end_y;
    rep(y, h) rep(x, w) {
        if (field[y][x] == 's') {
            start_x = x;
            start_y = y;
        } else if (field[y][x] == 'g') {
            end_x = x;
            end_y = y;
        }
    }

    dfs(start_y, start_x);

    if (seen[end_y][end_x])
        cout << "Yes" << endl;
    else
        cout << "No" << endl;

    return 0;
}

ARC061

atcoder.jp

1 以上 9 以下の数字のみからなる文字列 Sが与えられた時に.+を入れた時の和を計算する問題です.ただし,+が連続してはいけません.+を入れるか入れないかの2択なのでbit全探索を解く方がベターだと思いますが,全探索で練習しました.

以下のように考えました.左から数えて,木の深さに対応する桁に対して+をつけるか付けないかで全探索しました.

f:id:kanekou:20200806131429p:plain
入力値が125の場合

解いたコード

#include <algorithm>
#include <cmath>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <utility>
#include <vector>

using namespace std;
using ll = long long;
int N;

ll dfs(string s, int deep, int last, ll left) {
    // 深さが桁数-1で打ち切り
    if (deep == N) {
        return left + stoll(s.substr(last, s.size() - last));
    }

    // depp番目までを数に直す
    string s_sub = s.substr(last, deep + 1 - last);
    ll right = stoll(s_sub);

    auto sum1 = dfs(s, deep + 1, deep + 1, left + right);  // yes
    auto sum2 = dfs(s, deep + 1, last, left);              // no

    return sum1 + sum2;
}

int main() {
    string s;
    cin >> s;
    N = s.size() - 1;
    auto ans = dfs(s, 0, 0, 0);
    cout << ans << endl;
    return 0;
}

参考サイト shoman.hatenablog.com

最近

自分自身のキャリアについてなんとなく固まってきた気がするので,言語化しときます.

  1. 3年以内にプロダクトの技術選定に携わりたい,オーナーシップを持ってプロダクトを作り上げたい.

  2. チーム開発が好きなので,4-5年でスクラムマスターとして開発を回したい.

この二つです. 1が優先順位が高くて,当事者意識を持ってプロダクトを開発したいです.責任を持つのは怖いですがその分成長できると思うし,「自分がこのサービスを作ってるんだ!」というやりがいが人一倍感じられるんじゃないかと思います.技術力は頑張ってつけます.

2はインターン活動の中で自分自身を振り返って時に,やっぱり1人よりチーム開発が好きだと再認識したからです.ある程度の技術力がついたのちに,チーム全体の生産性をあげるための潤滑油として活躍したいです.

スクラムマスターしてたら開発できないんじゃないかとかありますが,何かしらプログラミングをする機会を別に作っていきたいです(OSS貢献とかしたい).チーム開発のノウハウを知っている開発者になりたいです.

技育祭2日目に参加しました

技育祭2日目参加したので所感を書き綴ります.

キャリア相談

CAの方々と就活について話す機会がありました.

インターン活動ではコーディングテストで実力を図ることが多いですが,実力とビジョンをどのくらいの割合でみているのか気になって質問しました.

CAとしては,「どのように成長したいか,目的を明確にしている人を重視している」とのことでした. その中でどのように行動してきたかを示すような証拠(ポートフォリオ)があるとよいとのことです. やはりポートフォリオは目に見えてわかりやすいですよね.

その時の技術力というよりは,将来のポテンシャルをみているようです.

将来的には技術を用いて社会問題,あるいは身の回りの生活の課題を解決したり最適化したいと考えているので,話を聞いていて色々挑戦できそうな環境だなという印象を受けました.

あとはインターンシップ交流会で同志たちと友達になりました. みんなサマーインターンに苦労しているようです.僕もそれなりに選考に落ちて自信を失っていたので少し励みになりました. 通常選考より倍率が高いらしいので,神経質にならず挑戦していきたいお気持ちです.. 通常選考の方が受かることも普通にあるっぽいですし(とは言ってもやっぱり落ちたら落ち込む).

プログラミングコンテストのすゝめ 〜競プロはキャリアであり、趣味でもあり、学びである〜

AtCoder社長のchokudaiさんの登壇がとても面白かったです. アルゴリズムを改良することで計算量を数千倍削減できることを,面白おかしくわかりやすく伝えてくださり,めちゃくちゃ盛り上がっていました. コメント欄がニコニコ生配信かな?って思うほどギャグ線に溢れていました.

なんちゃって解法がまさかのACした時,腹捩れるぐらい笑いました.

キャリアがどうこうっていうより競プロの楽しさが伝わってきたのがよかったです. 僕も競プロやっていますが,キャリアのためというよりは単純に楽しいのでやってる感じです.(まだ灰色の雑魚ーダーです)

インターンのコーディング試験で「競プロちょっとやっといてよかった〜」と思うこともあり,それなりに役立つと思っていますが,それだけのモチベだと続かないのかなと感じます. chokudaiさんは「競プロ楽しい」という感覚を前面に押し出していて,「課題,勉強」という捉え方のみで取り組むのではなく,ゲーム感覚で楽しむことが一番実力アップにも繋がるし,いい付き合い方なんだろなと思いました. とはいえ解けないとかかなり落ち込みます.毎週落ち込んでいます.精進しよう.

LT大会

小学生のLTがクオリティ高すぎて感動しました.

ソフトフェア開発をしていると実感するのですが,自分が思い描いたものを形にすることって難しいですよね. それを1人で,しかもハードウェアとソフトウェアの両方を小学生が自作したというのはすごくないですか? クオリティの高さもさることながら,ゲームをクリアするとは背面から「たけのこ」と「きのこ」が現れるあのアイディア性には驚きました.

加えてプレゼンもハキハキしてて,説明もわかりやすかった.(声もよかった)

将来は発明家?になりたいと言ってましたがほんとになれそう. 将来が楽しみすぎます.

変化し、進化し続ける技術者になるために 〜メルカリ・メルペイCTOの場合〜

印象深い言葉として,名村さんのおっしゃっていた「自分で決めて動くことが大切」という言葉です. 自分が責任を追うのが怖い,だから他人に決定を押し付けるマインドは成長に繋がらないということですね. そういう節があるかもしれない,と思ったので自分で意思決定してプロダクトを動かしていくことを早いうちから経験していきたいと思いました. 正直怖いけど,乗り越えて成長したいお気持ち.

モチベ

インターン活動をしているのですが,同志がどのような状況なのかよくわからなかったので,交流を通して状況を知ることができたし仲間もできました. 最近は自信喪失気味だったのですが「自分は1人ではない」という思うことができたのは大きかったのかなと思います. またトップレベルのCTOや人事の方,LT発表を通して刺激をもらうことができて,純粋に頑張ろうと思いました(小並感).

技育祭1日目参加しました

就活生として,技育祭に参加したので所感を書き綴っていきます.

talent.supporterz.jp

働く上でのキャリアについて

メルカリエンジニアのcodehexさんのお話を聞いて強く感じたことです.

私はcodehexの出身学部と同じところに所属していて,codehexさんはコミュニティを運用しつつ,コードもバリバリに書けるつよい方という認識でした.

今回話を聞いて感じたとは,多くのインプットと継続的なアウトプットが高いコーディング能力の土台となっている,ということです. 「つよい」と思っている人はそれなりにインプットとアウトプットをこなしていることを再認したし,やるかやらないかの話なんだよな,と考えることができて少し前向きになれた気がします. どこか「自分のような凡才とは持ってるものが違うんだろうな...」みたいなマイナス思考に捉えていた節があるので.明らかにインプット量もアウトプット量もつよつよな人たちに負けてるし,全然言い訳できないですね. はてなの大西さんの話でもあったのですが,とにかくアウトプットは重要だと.だから今もブログ書いてます.継続できる範囲でアウトプットしていきたい.

あと印象深かったのは,「就職前はコードさえ書ければ良いと考えていたが,仕事をこなすうちにチーム全体の生産性を考えるようになった」ということです. 私は今現在インターン活動真っ只中で,自分のキャリアに悩みはじめてます.第一にコードを書き続けていたい,けどマネジメントも学んでいった方が良いのではないかと考えています.

話を聞くうちに,最初はバリバリにコーディングしたいという思いは強くなりました. そして働く中でキャリアについて考え方の変化があるかもしれないし,そのときキャリア選択を変えても良いんじゃないかな,という考えに至りました. マネジメントは開発者としても最低限必要スキルだと思うので,PMに回ったときのためにもちょくちょく勉強していきたいです.

CTOパネルディスカッションでもCTOの方が仰ってたように,「昔はコードしか書きたくないと思ってたけど,働くうちにキャリア像が変化して今に到る」みたいなことは割とあるのかなと思いました. (考えてみれば大学入学前と入学後でもやりたいことまるで違います)

2日目も楽しむ

ギークな話もあればキャリアに関わる話もあり,いろいろ考える機会になりました. 明日2日目も参加するので楽しみです.

goのdatabase/sqlの使い方備忘録

最近Goを触る機会があり,ちょっとハマりかけたdatabase/sql packageの使い方をメモ.

import

import (
    "database/sql"
    _ "github.com/go-sql-driver/mysql"
)

コネクションの確立

var db *sql.DB

func main() {
    err := godotenv.Load()
    if err != nil {
        log.Println("Error loading .env file")
    }
    db, err = sql.Open("mysql", "root:"+os.Getenv("DB_PASSWORD")+"@tcp(127.0.0.1:3306)/bbs") //接続
    if err != nil {
        log.Println(err)
    }
    err = db.Ping() //接続確認
    if err != nil {
        log.Println(err)
    }
    defer db.Close()

sql発行例

func LoginHandler(w http.ResponseWriter, r *http.Request) {
        email := "hoge@hoge.com"
        err := db.QueryRow("select email from users where email = ?", email).Scan(&email) //発行
        if err != sql.ErrNoRows { 
          http.Redirect(w, r, "/index", http.StatusFound)
       }

注意点

Openは基本的に一回だけ

ドキュメント以下

The returned DB is safe for concurrent use by multiple goroutines and maintains its own pool of idle connections. Thus, the Open function should be called just once. It is rarely necessary to close a DB.

golang.org

書いてあるように,コネクションプールのおかげでsql.Open()の呼び出しは一回だけでいいっぽい. 最初hundlerごとにopen,closeしてたw

mainまたはinitで接続を確立し,コネクションプールで使い回す感じだと学びました.

接続確認にはdb.Ping()を呼び出す.

ドキュメント以下にあるように,db.Open()は引数の検証をするだけであるため接続確認はdb.Ping()を使用する.

Open may just validate its arguments without creating a connection to the database. To verify that the data source name is valid, call Ping.

所感

まだGolangを触り始めたばかりですが,小さいパーツからつくっていくこの感じ面白いです.

はやくチュートリアル終わらせて本格的なプロダクトをつくってみたいです. まだまだ知識不足なのでもっと勉強します.