20191001のGoに関する記事は6件です。

技術書典の本を検索できるアプリを作ってみた~サーバーサイド 編~

はじめに

この記事は、時間ができた学生が暇つぶしに「技術書典の本を検索できるアプリを作ってみた」というものです。
技術書典に参加する前にある程度購入する本を決めると思うのですが、その際に欲しい技術について書いてある本を探したいと思うはずです。サークル一覧から本を探すのはとても大変で時間が足りません。そこでスクレーピングしてデータ収集し、検索できるアプリを開発してみました(自己満)。
この記事ではサーバーサイドについて書きます。次の記事で「Androidアプリを作ってみた」を書きたいと思います。

サーバーサイドの流れ

  1. Pythonでデータを収集する
    I.技術書典のサークルリスト からURL情報を収集する
    II. 集めたURLにアクセスしてそれぞれのサークルが出している本の情報を収集する
  2. 収集したデータをDBに保存する
  3. Goでサーバーを立てる

1. Pythonでデータ収集

I. 技術書典のサークルリスト からURL情報を収集する

技術書典のサークルリストの<a>タグをひたすら保存するコード

collect_url.py
# coding: UTF-8
from bs4 import BeautifulSoup
from selenium import webdriver
import chromedriver_binary
from selenium.webdriver.chrome.options import Options

# ブラウザのオプションを格納する変数
options = Options()

# Headlessモードを有効にする
options.set_headless(True)

# ブラウザを起動する
driver = webdriver.Chrome(chrome_options=options)

# ブラウザでアクセスする
driver.get("https://techbookfest.org/event/tbf07/circle")

# HTMLを文字コードをUTF-8に変換してから取得
html = driver.page_source.encode('utf-8')

# BeautifulSoupで扱えるようにパース
soup = BeautifulSoup(html, "html.parser")

# ファイル出力
file_text = open("url_data.txt", "w")
elems = soup.find_all("a")
for elem in elems:
    print(elem.get('href'),file=file_text)
file_text.close()

II. 集めたURLにアクセスしてそれぞれのサークルが出している本の情報を収集する

1.で集めたURLにアクセスし、
出店しているサークル名、配置場所、ジャンル、ジャンル詳細、サークル画像、出品している本の名前、内容
のデータを収集する。(残念ながらペンネームは集められなかった。html にclass or idを貼って無いから面倒

collect_data.py
# coding: UTF-8
from bs4 import BeautifulSoup
from selenium import webdriver
import chromedriver_binary
from selenium.webdriver.chrome.options import Options
import pickle
import sys

# データを保存する
i = 0
sys.setrecursionlimit(10000)
with open('../data/getData.txt', 'wb') as ff:

    # 保存するデータ
    save_datas = []

    # ブラウザのオプションを格納する変数
    options = Options()

    # Headlessモードを有効にする
    options.set_headless(True)

    # ブラウザを起動する
    driver = webdriver.Chrome(chrome_options=options)

    urlHeader = "https://techbookfest.org"
    pathfile = "../data/url_data.txt"

    with open(pathfile) as f:
        for _path in f:
            i += 1
            url = urlHeader + _path
            print(i,url)
            # ブラウザでアクセスする
            driver.get(url)

            # HTMLを文字コードをUTF-8に変換してから取得
            html = driver.page_source.encode('utf-8')

            # BeautifulSoupで扱えるようにパース
            soup = BeautifulSoup(html, "html.parser")

            circle = soup.find(class_="circle-name").string
            arrange = soup.find(class_="circle-space-label").string
            genre = soup.find(class_="circle-genre-label").string
            keyword = soup.find(class_="circle-genre-free-format").string

            circle_image = soup.find(class_="circle-detail-image").find(class_="ng-star-inserted")

            book_title = []
            for a in soup.find_all(class_="mat-card-title"):
                book_title.append(a.string)

            book_content = []
            for a in soup.find_all(class_="products-description"):
                book_content.append(a.string)

            for title, content in zip(book_title, book_content):
                data = [circle,circle_image['src'],arrange,genre,keyword,title,content,url]
                save_datas.append(data)

    pickle.dump(save_datas,ff)

2. 収集したデータをDBに保存する

ファイルに保存したデータを取得し、MySQLにInsertするだけのプログラムです。Insertするだけなので適当なプログラムになってしまった。

insertDB.py
# coding: UTF-8
import MySQLdb
import pickle

# データベースへの接続とカーソルの生成
connection = MySQLdb.connect(
    host='0.0.0.0',
    user='user',
    passwd='password',
    db='techBook')
cursor = connection.cursor()

# id, circle, circle_image, arr, genere, keyword, title, content
with open('../data/getData.txt','rb') as f:
    load_datas = pickle.load(f)
    for load_data in load_datas:
        data = []
        if load_data[6] == None:
            for dd in load_data:
                if dd == None:
                    continue
                dd = dd.replace('\'','')
                data.append(dd)
            sql = "INSERT INTO circle (circle, circle_image, arr, genere, keyword, title, circle_url) values ('" + data[0] + "','" + data[1] + "','" + data[2]+"','" + data[3]+"','" + data[4]+"','" + data[5]+"','" + data[6]+"')"
        else:
            for dd in load_data:
                dd = dd.replace('\'','')
                data.append(dd)
            sql = "INSERT INTO circle (circle, circle_image, arr, genere, keyword, title, content, circle_url) values ('" + data[0] + "','" + data[1] + "','" + data[2]+"','" + data[3]+"','" + data[4]+"','" + data[5]+"','" + data[6]+"','" + data[7]+"')"
            print(sql)
        cursor.execute(sql)

# 保存を実行
connection.commit()

# 接続を閉じる
connection.close()

3. Goでサーバーを立てる

Pythonで書いてもよかったのですが、気分的にGoで書きました。
レイアードアーキテクチャで書いています。(宣伝)
ファイル数が多いので全部載せることはできませんでした。Githubを参照ください。
検索はSQLの部分一致検索で行います。

SELECT * FROM circle where content like '%swift%';

取得したデータをjson形式で返して終わりです!!

[request] 
{
"keyword":"..."
}

[response]
{
    "result": [
        {
            "CircleURL": "...",
            "Circle": "...",
            "CircleImage": "...",
            "arr": "...",
            "Genere": "...",
            "Keyword": "...",
            "Title": "...",
            "Content": "..."
        },
    ]
}

終わりに

久々にPythonを書いた気がします。全体的に書いてて楽しかったです。今回書いたコードはこちら
読んでいただきありがとうございました。次回のAndroid編をお楽しみに!!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

技術書典の本を検索できるアプリを作ってみた~サーバーサイド 編(Python, Go)~

はじめに

この記事は、時間ができた学生が暇つぶしに「技術書典の本を検索できるアプリを作ってみた」というものです。
技術書典に参加する前にある程度購入する本を決めると思うのですが、その際に欲しい技術について書いてある本を探したいと思うはずです。サークル一覧から本を探すのはとても大変で時間が足りません。そこでスクレーピングしてデータ収集し、検索できるアプリを開発してみました(自己満)。
この記事ではサーバーサイドについて書きます。次の記事で「Androidアプリを作ってみた」を書きたいと思います。

サーバーサイドの流れ

  1. Pythonでデータを収集する
    I.技術書典のサークルリスト からURL情報を収集する
    II. 集めたURLにアクセスしてそれぞれのサークルが出している本の情報を収集する
  2. 収集したデータをDBに保存する
  3. Goでサーバーを立てる

1. Pythonでデータ収集

I. 技術書典のサークルリスト からURL情報を収集する

技術書典のサークルリストの<a>タグをひたすら保存するコード

collect_url.py
# coding: UTF-8
from bs4 import BeautifulSoup
from selenium import webdriver
import chromedriver_binary
from selenium.webdriver.chrome.options import Options

# ブラウザのオプションを格納する変数
options = Options()

# Headlessモードを有効にする
options.set_headless(True)

# ブラウザを起動する
driver = webdriver.Chrome(chrome_options=options)

# ブラウザでアクセスする
driver.get("https://techbookfest.org/event/tbf07/circle")

# HTMLを文字コードをUTF-8に変換してから取得
html = driver.page_source.encode('utf-8')

# BeautifulSoupで扱えるようにパース
soup = BeautifulSoup(html, "html.parser")

# ファイル出力
file_text = open("url_data.txt", "w")
elems = soup.find_all("a")
for elem in elems:
    print(elem.get('href'),file=file_text)
file_text.close()

II. 集めたURLにアクセスしてそれぞれのサークルが出している本の情報を収集する

1.で集めたURLにアクセスし、
出店しているサークル名、配置場所、ジャンル、ジャンル詳細、サークル画像、出品している本の名前、内容
のデータを収集する。(残念ながらペンネームは集められなかった。html にclass or idを貼って無いから面倒

collect_data.py
# coding: UTF-8
from bs4 import BeautifulSoup
from selenium import webdriver
import chromedriver_binary
from selenium.webdriver.chrome.options import Options
import pickle
import sys

# データを保存する
i = 0
sys.setrecursionlimit(10000)
with open('../data/getData.txt', 'wb') as ff:

    # 保存するデータ
    save_datas = []

    # ブラウザのオプションを格納する変数
    options = Options()

    # Headlessモードを有効にする
    options.set_headless(True)

    # ブラウザを起動する
    driver = webdriver.Chrome(chrome_options=options)

    urlHeader = "https://techbookfest.org"
    pathfile = "../data/url_data.txt"

    with open(pathfile) as f:
        for _path in f:
            i += 1
            url = urlHeader + _path
            print(i,url)
            # ブラウザでアクセスする
            driver.get(url)

            # HTMLを文字コードをUTF-8に変換してから取得
            html = driver.page_source.encode('utf-8')

            # BeautifulSoupで扱えるようにパース
            soup = BeautifulSoup(html, "html.parser")

            circle = soup.find(class_="circle-name").string
            arrange = soup.find(class_="circle-space-label").string
            genre = soup.find(class_="circle-genre-label").string
            keyword = soup.find(class_="circle-genre-free-format").string

            circle_image = soup.find(class_="circle-detail-image").find(class_="ng-star-inserted")

            book_title = []
            for a in soup.find_all(class_="mat-card-title"):
                book_title.append(a.string)

            book_content = []
            for a in soup.find_all(class_="products-description"):
                book_content.append(a.string)

            for title, content in zip(book_title, book_content):
                data = [circle,circle_image['src'],arrange,genre,keyword,title,content,url]
                save_datas.append(data)

    pickle.dump(save_datas,ff)

2. 収集したデータをDBに保存する

ファイルに保存したデータを取得し、MySQLにInsertするだけのプログラムです。Insertするだけなので適当なプログラムになってしまった。

insertDB.py
# coding: UTF-8
import MySQLdb
import pickle

# データベースへの接続とカーソルの生成
connection = MySQLdb.connect(
    host='0.0.0.0',
    user='user',
    passwd='password',
    db='techBook')
cursor = connection.cursor()

# id, circle, circle_image, arr, genere, keyword, title, content
with open('../data/getData.txt','rb') as f:
    load_datas = pickle.load(f)
    for load_data in load_datas:
        data = []
        if load_data[6] == None:
            for dd in load_data:
                if dd == None:
                    continue
                dd = dd.replace('\'','')
                data.append(dd)
            sql = "INSERT INTO circle (circle, circle_image, arr, genere, keyword, title, circle_url) values ('" + data[0] + "','" + data[1] + "','" + data[2]+"','" + data[3]+"','" + data[4]+"','" + data[5]+"','" + data[6]+"')"
        else:
            for dd in load_data:
                dd = dd.replace('\'','')
                data.append(dd)
            sql = "INSERT INTO circle (circle, circle_image, arr, genere, keyword, title, content, circle_url) values ('" + data[0] + "','" + data[1] + "','" + data[2]+"','" + data[3]+"','" + data[4]+"','" + data[5]+"','" + data[6]+"','" + data[7]+"')"
            print(sql)
        cursor.execute(sql)

# 保存を実行
connection.commit()

# 接続を閉じる
connection.close()

3. Goでサーバーを立てる

Pythonで書いてもよかったのですが、気分的にGoで書きました。
レイアードアーキテクチャで書いています。(宣伝)
ファイル数が多いので全部載せることはできませんでした。Githubを参照ください。
検索はSQLの部分一致検索で行います。

SELECT * FROM circle where content like '%swift%';

取得したデータをjson形式で返して終わりです!!

[request] 
{
"keyword":"..."
}

[response]
{
    "result": [
        {
            "CircleURL": "...",
            "Circle": "...",
            "CircleImage": "...",
            "arr": "...",
            "Genere": "...",
            "Keyword": "...",
            "Title": "...",
            "Content": "..."
        },
    ]
}

終わりに

久々にPythonを書いた気がします。全体的に書いてて楽しかったです。今回書いたコードはこちら
読んでいただきありがとうございました。次回のAndroid編をお楽しみに!!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

CGOを含むgolangのプログラムをラズパイ向けにクロスビルドする

Ubuntu 18.04 を使用しています。

CGOを含む以下のプログラムをgolangのプログラムをラズパイ向けにクロスビルドするには

cgo_test.go
package main

import (
    "fmt"
)

// #cgo LDFLAGS: -lm
// #include <math.h>
import "C"

func main() {
    fmt.Printf("sqrt(3) = %f\n", C.sqrt(C.double(3)))
}

まずクロスコンパイラのインストール

sudo apt install g++-arm-linux-gnueabihf

gccしかいらなくてもあえてg++をインストールすることでクロスのlibcなど必要なものが全て芋づる式にインストールされます。

go build では以下のように環境変数を指定します。

CC=arm-linux-gnueabihf-gcc GOOS=linux GOARCH=arm GOARM=6 CGO_ENABLED=1 go build -o cgo_test.armv6

ポイントはCC= でCのクロスコンパイラを指定することと、CGO_ENABLED=1 をつけること。

詳しくは
https://golang.org/cmd/cgo/

関連

x86_64のUbuntuでC/C++のソースコードをARM/ARM64用にクロスコンパイルしてQEMUで実行する方法のまとめ

昔はARM用にビルドしたgolangの実行ファイルはqemuでは動かなかったのですが、今はそんなことなくてqemuで動かすことができるようになっています。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

熨斗(のし)の王様を作ったときに気をつけたこと

まとめ

  • のし王というWebサービスを作った。 のし王サムネイル
  • のし作成サービスが既に存在する中で、下記に気をつけたというお話。
    • サービス作成のきっかけになった不満点・ニーズをもれなく仕様に落とし込む
    • 仕様策定時は技術的な楽さに甘えず、ユーザー体験を優先する
    • 上記を実現するため必要最低限の技術を選定する

不満点の解消、ニーズを仕様に落とし込む

  • ちょっとした贈り物に「のし」をつけたかった。
  • 既存サービスは、以下の点で不満が残った。
    • 「のし紙」のテンプレート(背景画像)だけが提供されており、Wordなどに貼り付けた後、自分で文字を描かないとダメだった
    • Web上で任意の文字まで入れられるサービスでは、最後になるまで仕上がり具合がが分からなかった
    • 最終画像にサービス名のロゴが入ってしまい、贈り物には不向きだった
    • フォントがしょぼかった
    • 水引き(背景画像)や表書き(御礼などの上部の文字)をどう選んでよいか分からなかった
  • 不満点の解消をもれなく仕様に落とし込んだ。
    • 背景画像と任意の文字を組み合わせてPDFで出力できるようにする
    • 最終出力結果には広告を入れない
    • 有料でもカッコいいフォントを採用する
    • 用途に応じて自動で「水引き」と「表書き」が選択できるようにする
  • マッスルの神様 = マ神 → 熨斗の王様 = のし王

ユーザー体験を最優先する

技術的な楽さを優先 ユーザー体験を優先
文字描画 ユーザー自身がWordなどで描画 Webサービス上で描画
仕上がり確認 プレビュー不可 or プレビューボタンクリックで表示 リアルタイムプレビュー表示
水引き、表書き ユーザーの選択したものを表示 用途を選ぶと最適な水引き、表書きを自動で選択
描画フォント 無料の範囲で選択可能 有料で質の良いフォントが選択可能
ユーザーが泥酔状態 酔いが冷めてから使う 泥酔状態でも直感で使える

仕様の実現方法を考える

  • 使用した技術
    • Webサービス上で文字描画しPDF出力
      • 水引きの種類や表書きなどの情報をブラウザ上で選択させる
      • サーバーでPDFを作成してダウンロードさせる(Golang/Google App Engine)
    • リアルタイムプレビュー表示
      • サムネイル描画サーバー(Golang/Google App Engine)を準備
      • 水引きの種類や表書きなどの情報が変更されたらAjax(jQuery)でサーバーからサムネイル画像を取得し、ブラウザ上に即時反映
    • 用途を選ぶと最適な水引き、表書きを自動で選択
      • Bulma-ExtensionsのQuickViewで用途一覧を表示
      • 用途を入力させて絞り込み(jQuery)
      • 用途クリックで「水引き」と「表書き」を最適なものに変更(jQuery)
  • 選定理由
    • 仕様実現に際して最低限/シンプルなものを選ぶ
    • 最新の技術よりも枯れて安定した技術を選ぶ
    • なるべくメンテナンスが不要なものを選ぶ
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Golang - Go Modulesで開発環境の用意する

はじめに

今まで以下の流れで開発環境を用意しました。

GoModulesの存在を知り、以下の理由からもう一度開発環境を見直すことにしました。

  • バージョン管理のデファクトスタンダードになる(なってる?)GoModulesを理解したかった
  • GoModulesを使用することで、設定に苦しんだGOPATH関連の制限から解放されそうだった

Go Modules

GoModulesは、Go1.11から導入され始めたGoの新しいバージョン管理システムになります。
詳細については、以下を参照いただくのが良いと思います。
GoModulesの概要から、今までのバージョン管理との違いまで把握できます。

GOPATH mode から module-aware mode

今まで(GOPATH mode)は標準pkg以外を全部 $GOPATH 以下のディレクトリで管理する仕様となっており
プロジェクトも $GOPATH/src 配下に作成する必要がありました。この制約で上記の開発環境を用意する際にかなり苦しみました。

しかし、module-aware mode では標準pkg以外の全てのパッケージをモジュールとして扱い、モジュールの管理やビルドが任意のディレクトリで可能になりました。($GOPATH/src にプロジェクトを配置しちゃダメというわけではなそう)

環境構築

go(1.10以降)とVSCodeはインストール済みの状態から始めたいと思います
私はgo(1.13.1)、VSCode(1.38.1)でやっています

環境変数

.zshrc(Shellによる)
export PATH=$GOPATH/bin:$PATH
export GOPATH=$HOME/go
export GO111MODULE=on

※GOPATHはプロジェクトと違うディレクトリで試したかったので $HOME/go にしています

GO111MODULE はGOPATH modeとmodule-aware modeを切り替えるための環境変数です。
Go 1.13からデフォルトが on になる予定だったようですが、どうやら auto がデフォルトになっているそうです。
そのため環境変数に上記を設定します。

  • on :Go modules を使う (module-aware mode)
  • off :$GOPATH を使う (GOPATH mode)
  • auto :$GOPATH/src の外に対象のリポジトリがあり、 go.mod が存在する場合は module-aware mode、そうでない場合は GOPATH mode

ディレクトリの作成

以下の構成でディレクトリを作成します

参考:Modules#quick-start

$  tree -F GolangProjects
GolangProjects
└── github.com/
    └── so-heee/
        └── modules_example/

モジュールの初期化

以下のコマンドでモジュールを初期化します
初期化すると go.mod ファイル(モジュールを管理するファイル)が出来ます

$ go mod init github.com/so-hee/modules_example
go: creating new go.mod: module github.com/so-hee/modules_example
└── modules_example
    └── go.mod

サンプルプログラムの作成

hello.go
package main

import (
    "fmt"
    "rsc.io/quote"
)

func main() {
    fmt.Println(quote.Hello())
}

ビルドと実行

作成したファイルをビルドすると、依存パッケージである rsc.io/quote がダウンロードされます

$ go build -o hello
go: finding rsc.io/quote v1.5.2
go: downloading rsc.io/quote v1.5.2
go: extracting rsc.io/quote v1.5.2
go: downloading rsc.io/sampler v1.3.0
go: extracting rsc.io/sampler v1.3.0
go: downloading golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c
go: extracting golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c
go: finding rsc.io/sampler v1.3.0
go: finding golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c

GOPATHに設定したディレクトリにパッケージがダウンロードされています

$ tree -F go -L 3 -d
go
└── pkg
    └── mod
        ├── cache
        ├── golang.org
        └── rsc.io

また go.sum ファイル(依存モジュールのチェックサムの管理をしてるファイル)が作成されています
ビルドしたファイルを実行してみます

$ ./hello
Hello, world.

VSCodeでの環境設定

上記のフォルダをVSCodeで開きます

拡張機能インストール

Cmd + Shif + x でExtensionMarketplaceを開いてGoをインストール

スクリーンショット 2019-09-22 20.03.18.png

先ほど作成したhello.goのファイルを開くとツールのパッケージをインストールするか聞かれるのでインストールしましょう

スクリーンショット 2019-10-01 16.15.05.png

聞かれない場合がもしあった場合は、Cmd + Shift + P でコマンドパレットを表示
GO: Install/Update Tools で全て選択してインストールしましょう

独自パッケージの作成

新規でパッケージを作成し、mainでimportしてみます

modules_example/calc/calc.go
package calc

// Sum は引数a, bを合算して返却します
func Sum(a, b int) int {
    return a + b
}
hello.go
package main

import (
    "fmt"

    "github.com/so-heee/modules_example/calc"
    "rsc.io/quote"
)

func main() {
    fmt.Println(quote.Hello())
    fmt.Println(calc.Sum(1, 2))
}

問題なくimportできたので、実行してみます。F5でデバッグしてみます

Hello, world.
3

Dockerを使った開発環境の設定

dockerのインストール

Homebrewでdockerをインストールします(Homebrewの使い方は省略します)

$ brew cask install docker
Updating Homebrew...
==> Auto-updated Homebrew!
Updated 2 taps (homebrew/core and homebrew/cask).
==> Updated Formulae
dbmate                kops                  mosquitto            rabbitmq

==> Satisfying dependencies
==> Downloading https://download.docker.com/mac/stable/38240/Docker.dmg
######################################################################## 100.0%
==> Verifying SHA-256 checksum for Cask 'docker'.
==> Installing Cask docker
==> Moving App 'Docker.app' to '/Applications/Docker.app'.
?  docker was successfully installed!

$ docker --version
Docker version 19.03.2, build 6a30dfc

VSCodeにRemote-Containersのインストール

Cmd + Shif + x でExtensionMarketplaceを開いて Remote-Containers を検索してインストールします

スクリーンショット 2019-09-26 12.36.38.png

設定ファイルの作成

左下の矢印アイコンを選択します

スクリーンショット 2019-09-26 12.33.32.png

今度は Remote-Containers: Add Development Container Configuration Files を選択します
何の開発環境を作るか聞かれるためGoを選択します

スクリーンショット 2019-09-26 17.33.21.png

選択すると先ほどのサンプルであった.devcontainerディレクトリが作成され
その下にdevcontainer.jsonDockerfileが入っています

スクリーンショット 2019-10-01 17.03.23.png

Dockerコンテナの作成

左下の矢印アイコンを選択します

Remote-Containers: Open Folder in Container を選択し、フォルダを選択します
スクリーンショット 2019-09-26 12.33.09.png

ビルドが開始されます

スクリーンショット 2019-09-26 13.56.13.png

スクリーンショット 2019-09-26 17.41.31.png

ビルド中にdetailsを押すと、ターミナルにビルドの状況が表示されます

完了するとエクスプローラーに表示されます

スクリーンショット 2019-10-01 17.13.00.png

コンテナ上でGOのプログラムを実行

ターミナルタブを開いて、右側のプルダウンを 1:bash にします
hello.goを実行してみます

するとコンテナ上のGOPATH(devcontainer.jsonのgo:gopath)に依存パッケージがダウンロードされます
ローカルの時と同じですね。

root@4f93101644d1:/workspaces/GolangProjects/github.com/so-heee/modules_example# go run ./hello.go 
go: downloading rsc.io/quote v1.5.2
go: extracting rsc.io/quote v1.5.2
go: downloading rsc.io/sampler v1.3.0
go: extracting rsc.io/sampler v1.3.0
go: downloading golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c
go: extracting golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c
go: finding rsc.io/quote v1.5.2
go: finding rsc.io/sampler v1.3.0
go: finding golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c
こんにちは世界。
3

※こんにちは世界になるのは別の問題だと思うので今回は無視します(F5実行は問題ありません)

課題(依存先モジュールの編集する時)

githubで公開すると、基本的にそちらを参照してしまうため、ローカルで修正しても動作が変わりませんでした。。。
対応策としてgo.modを一時的に書き換えてreplaceディレクティブを使う方法や、go mod vendor でvendorを書き出してしまってそれを編集するなどがあるようですが、そもそも開発スタイルが正しいのか不安が残ります。
こちら解決策ご存知の方、もしくは別の方法をとってる方などいましたら是非ご教示ください!!

参考:最近のGo Modulesプラクティス ~ ghqユーザーの場合も添えて

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Go言語版 競技プログラミング用標準入力関数を書いてみた

概要

fmt.scanでは、大量入力時に動作が遅いという事なので、入力数$10^5$に対応する関数を書いてみました。

      バージョン 備考
対応バージョン 1.6以上 Scanner.Buffer関数の対応バージョン
AtCoder 1.6 2019年10月01日現在
Codeforces 1.12.6 2019年10月01日現在

基本的には、
Go 言語で標準入力から読み込む競技プログラミングのアレ --- 改訂第二版
の「bufioScanner を使う (2) スペース区切りで読み込む」と同じですが、改良して長大文字列をScannerで読めるようにします。

MaxScanTokenSizeの限界を突破する

デフォルトのScannerではMaxScanTokenSize=65536文字を超える文字列は読み込めなかったのですが、バージョン1.6でScanner.Buffer関数が追加されました。この関数で使用するバッファと最大読み込み文字数を変更することができます。

作成コード

import (
   "os"
   "bufio"
   "strconv"
)

const MaxBuf = 200100
var buf []byte = make([]byte,MaxBuf)
var sc = bufio.NewScanner(os.Stdin)

func init() {
    sc.Split(bufio.ScanWords)
    sc.Buffer(buf,MaxBuf)
}

func readString() string {
   sc.Scan()
   return sc.Text()
}

func readInt() int {
    sc.Scan()
    r,_ := strconv.Atoi(sc.Text())
    return r
}

func readInt64() int64 {
   sc.Scan()
   r,_ := strconv.ParseInt(sc.Text(),10,64)
   return r
}

func readFloat64() float64 {
    sc.Scan()
    r,_ := strconv.ParseFloat(sc.Text(),64)
    return r
}

func init()は、func main()の前に実行され、パッケージ変数で初期化できなかった処理を行います。

  • sc.Split(bufio.ScanWords)
    • 空白文字区切りで文字列を読み込むよう設定します。
  • sc.Buffer(buf,MaxBuf)
    • 前述の通り、読み込みバッファと最大サイズを変更します。

string型の読み込み

sc.Scan()で読み込んだ結果をそのまま返します。

int型の読み込み

n := 1と書くと、nint型と型推論されます。int型とint64型の比較をしようとするとコンパイルエラーとなるため、整数は特に理由がない限りint型として読み込みたいです。strconv.Atoi関数を使用して文字列をint型に変換します。競技プログラミングでは入力が欠落することがないため、エラー処理は行いません。

int64型の読み込み

int型のビット数は実装によって違うことがあるため、64ビット整数であることを保証するint64型で読み込みたいことがあります。strconv.ParseInt関数を使用します。

float64型の読み込み

f := 3.14と書くと、ffloat64型と型推論されます。float32型を使う理由は特にないため、float64型の読み込み関数のみ用意します。strconv.ParseFloat関数を使用します。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む