Skip to content

Commit

Permalink
🌱 初回コミット
Browse files Browse the repository at this point in the history
  • Loading branch information
toriwasa committed Oct 15, 2023
0 parents commit 5cec17e
Show file tree
Hide file tree
Showing 12 changed files with 328 additions and 0 deletions.
6 changes: 6 additions & 0 deletions .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# サービスのビルド環境および実行環境のコンテナ
FROM mcr.microsoft.com/vscode/devcontainers/go:1
WORKDIR /workspace

# 作業ディレクトリ配下をコンテナ側の作業用ディレクトリにコピーする
COPY ./ .
12 changes: 12 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"name": "golang-workspace",
"dockerComposeFile": [
"docker-compose.dev.yml"
],
"service": "golang-dev",
"postCreateCommand": [
"bash",
".devcontainer/postCreateCommand.sh"
],
"workspaceFolder": "/workspace"
}
15 changes: 15 additions & 0 deletions .devcontainer/docker-compose.dev.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
version: "3"
# コンテナプロセスが終了しても作業内容が永続するためのデータボリュームを定義しておく
volumes:
workspace-data:
services:
golang-dev:
# Dockerfileからプロジェクトディレクトリ配下の全てのファイルを参照できるようにプロジェクトディレクトリ直下をcontextに指定する
build:
context: ../
dockerfile: .devcontainer/Dockerfile
volumes:
# 開発に利用するソースコードやビルド結果はホスト側のデータボリュームで管理する
- workspace-data:/workspace
# 開発環境用にプロセスを起動し続ける
tty: true
4 changes: 4 additions & 0 deletions .devcontainer/postCreateCommand.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/bash

# VSCodeで扱うディレクトリの所有者を Dev Container のデフォルトユーザーである vscode ユーザーに変更する
sudo chown -R vscode:vscode /workspace
27 changes: 27 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: release
on:
push:
tags:
- "v[0-9]+.[0-9]+.[0-9]+"
jobs:
goreleaser:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0
- run: git fetch --force --tags
- name: Setup Go
uses: actions/setup-go@v4
with:
go-version: '1.20'
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v5
with:
# either 'goreleaser' (default) or 'goreleaser-pro':
distribution: goreleaser
version: latest
args: release --clean
env:
GITHUB_TOKEN: ${{ secrets.GROUPING_REPOSITORY_ACCESS_TOKEN }}
24 changes: 24 additions & 0 deletions .goreleaser.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
project_name: grouping
builds:
- env: [CGO_ENABLED=0]
main: ./cmd/grouping/main.go
goos:
- linux
- windows
- darwin
goarch:
- amd64
- arm64
archives:
- format_overrides:
- goos: windows
format: zip
nfpms:
- maintainer: toriwasa <[email protected]>
description: This is a tool to group sequence of numbers.
homepage: https://github.com/toriwasa/
license: MIT
formats:
- deb
- rpm
- apk
16 changes: 16 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
// IntelliSense を使用して利用可能な属性を学べます。
// 既存の属性の説明をホバーして表示します。
// 詳細情報は次を確認してください: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Launch Package",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${workspaceFolder}/cmd/grouping/main.go"
, "args": ["-n", "30", "-g", "4"]
}
]
}
5 changes: 5 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"go.toolsManagement.checkForUpdates": "local",
"go.useLanguageServer": true,
"go.gopath": "/go"
}
34 changes: 34 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# README

## Overview
- n個の連番をランダムにg個のグループに振り分けるCLIツール
- グループごとに指定した区切り文字で連結した文字列を標準出力する

## Usage

```txt
Show Help
$ grouping --help
Usage:
$ grouping -n <number of elements> -g <number of groups> -d <delimiter> -v
Example:
$ grouping -n 10 -g 4 -d ","
3,4,9
2,5,6
7,8
0,1
```

## How to Build
- VSCode で このリポジトリを開く
- .devcontainer 配下のファイルを利用して開発コンテナを開く
- 起動したコンテナ上のbashで以下のビルドコマンドを実行する

```bash
# Linux向けビルド
go build -o grouping cmd/grouping/main.go
# Winodws向けビルド
GOOS=windows GOARCH=amd64 go build -o grouping.exe cmd/grouping/main.go
```
114 changes: 114 additions & 0 deletions app/grouping/grouping.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package grouping

import (
"fmt"
"log"
"math/rand"
"sort"
)

// 0から始まるn個の連番をランダムに返すイテレータを返却する
func generateRandomIntIterator(n int) func() (int, error) {
// n個の要素を持つスライスを生成する
seq := make([]int, n)
log.Printf("seq: %v\n", seq)

// 0, 1, 2, .. n の連番を要素として持つようにスライスを初期化する
for i := range seq {
seq[i] = i
}
log.Printf("seq: %v\n", seq)

// スライスの中身をランダムに並び替える
rand.Shuffle(len(seq), func(i, j int) {
seq[i], seq[j] = seq[j], seq[i]
})
log.Printf("seq: %v\n", seq)

// インデックスを管理する変数を定義する
index := -1
// スライスの要素を1つ返す関数を返却する
return func() (int, error) {
index++
if index >= len(seq) {
return 0, fmt.Errorf("index out of range")
}
return seq[index], nil
}
}

// イテレータからn個の値を取り出して昇順ソートしてから指定された区切り文字で分割された文字列にして返却する
func takeNSortedSeparated(iter func() (int, error), n int, delimiter string) (string, error) {
// イテレータからn個の値を取り出してseqスライスに格納する
seq := make([]int, n)
log.Printf("seq: %v\n", seq)

for i := 0; i < n; i++ {
v, err := iter()
if err != nil {
return "", err
}
seq[i] = v
}
log.Printf("seq: %v\n", seq)

// seqスライスを昇順ソートする
sort.Slice(seq, func(i, j int) bool {
return seq[i] < seq[j]
})
log.Printf("seq: %v\n", seq)

// seqスライスの要素を区切り文字で連結した文字列を返却する
s := ""
for i := 0; i < n; i++ {
s += fmt.Sprintf("%d%s", seq[i], delimiter)
}
// 末尾の区切り文字を削除する
s = s[:len(s)-len(delimiter)]

return s, nil
}

// n個の連番をランダムに並び替えた配列をg個のグループに分けて、各グループの要素を区切り文字で分割された文字列にしたイテレータを返却する
func GenerateGroupedRandomSeqIterator(n int, g int, delimiter string) (func() (string, error), error) {
// n は自然数であることを前提とする
if n <= 0 {
return nil, fmt.Errorf("n must be positive, but %d", n)
}
// g は自然数であることを前提とする
if g <= 0 {
return nil, fmt.Errorf("g must be positive, but %d", g)
}

// 最小グループサイズを計算する
minGroupSize := n / g
// 最大要素数のグループ数を計算する
maxElementsGroupCount := n % g
log.Printf("minGroupSize: %d\n", minGroupSize)
log.Printf("maxElementsGroupCount: %d\n", maxElementsGroupCount)

iter := generateRandomIntIterator(n)

outputGroupCount := -1
// グループの数だけ区切り文字で連結した文字列を返すイテレータを返却する
return func() (string, error) {
outputGroupCount++
// イテレータのループ上限はグループ数に等しい
if outputGroupCount >= g {
return "", fmt.Errorf("outputGroupCount out of range")
}

// 1グループあたり要素数のデフォルトは最小グループサイズ
groupSize := minGroupSize
// 最大要素数のグループ数に達するまでは、1グループあたり要素数を1つ増やす
if outputGroupCount < maxElementsGroupCount {
groupSize++
}
// 1グループ分の文字列要素を取得して返却する
s, err := takeNSortedSeparated(iter, groupSize, delimiter)
if err != nil {
return "", err
}
return s, nil
}, nil
}
68 changes: 68 additions & 0 deletions cmd/grouping/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package main

import (
"flag"
"io"
"log"

"github.com/toriwasa/grouping/app/grouping"
)

func main() {
// DEBUGログのフォーマットを設定
log.SetFlags(log.LstdFlags | log.Lshortfile)
// DEBUGログのプレフィックスを設定
log.SetPrefix("DEBUG: ")

// コマンドライン引数を解析する。 -n, -g, -d, -v というオプションを定義する
var n, g int
var d string
var isVerbose bool
// n と g はそれぞれ要素数とグループ数を表す
// デフォルト値はそれぞれ 10 と 4 である
flag.IntVar(&n, "n", 10, "number of elements")
flag.IntVar(&g, "g", 4, "number of groups")

// d は区切り文字を表す
// デフォルト値はタブ文字である
flag.StringVar(&d, "d", "\t", "delimiter")

// v はログを冗長に出力するモードを表す
// デフォルト値は false である
flag.BoolVar(&isVerbose, "v", false, "output verbose log")

// --help オプションをカスタマイズする
flag.Usage = func() {
println("Usage: grouping -n <number of elements> -g <number of groups> -d <delimiter> -v")
println("Example: grouping -n 10 -g 4 -d \",\"")
println("Description: generate random sequence and group them")
println("Options:")
flag.PrintDefaults()
}

// コマンドライン引数を解析する
flag.Parse()

// verbose モードでない場合はログを出力しない
if !isVerbose {
log.SetOutput(io.Discard)
}

// コマンドライン引数を出力する
log.Printf("n: %d, g: %d, d: \"%s\", v: %t\n", n, g, d, isVerbose)

// 引数に応じた文字列のイテレータを生成する
iter, err := grouping.GenerateGroupedRandomSeqIterator(n, g, d)
// エラーが発生した場合は終了する
if err != nil {
panic(err)
}
// イテレータから値を取り出して出力する
for {
s, err := iter()
if err != nil {
break
}
println(s)
}
}
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module github.com/toriwasa/grouping

go 1.20

0 comments on commit 5cec17e

Please sign in to comment.