goft - 解答

実装の全体像

以下に goft の想定解答を示します。各関数は禁止されているライブラリを使用せず、基本的な操作のみで実装しています。

go.mod

module goft

go 1.21

goft.go - メイン実装

package goft

// ============================================
// Part 1: 文字判定関数
// ============================================

// IsAlpha はアルファベットかどうかを判定します
func IsAlpha(r rune) bool {
	return IsUpper(r) || IsLower(r)
}

// IsDigit は数字かどうかを判定します
func IsDigit(r rune) bool {
	return r >= '0' && r <= '9'
}

// IsAlnum は英数字かどうかを判定します
func IsAlnum(r rune) bool {
	return IsAlpha(r) || IsDigit(r)
}

// IsSpace は空白文字かどうかを判定します
func IsSpace(r rune) bool {
	return r == ' ' || r == '\t' || r == '\n' ||
		r == '\r' || r == '\v' || r == '\f'
}

// IsPrint は印字可能文字かどうかを判定します
func IsPrint(r rune) bool {
	return r >= 0x20 && r <= 0x7E
}

// IsUpper は大文字かどうかを判定します
func IsUpper(r rune) bool {
	return r >= 'A' && r <= 'Z'
}

// IsLower は小文字かどうかを判定します
func IsLower(r rune) bool {
	return r >= 'a' && r <= 'z'
}

// ToUpper は小文字を大文字に変換します
func ToUpper(r rune) rune {
	if IsLower(r) {
		return r - 'a' + 'A'
	}
	return r
}

// ToLower は大文字を小文字に変換します
func ToLower(r rune) rune {
	if IsUpper(r) {
		return r - 'A' + 'a'
	}
	return r
}

// ============================================
// Part 2: 文字列操作関数
// ============================================

// Strlen は文字列の長さ(rune数)を返します
func Strlen(s string) int {
	count := 0
	for range s {
		count++
	}
	return count
}

// Strchr は文字列内で指定された文字を先頭から検索します
// 見つかった場合はインデックス、見つからない場合は-1を返します
func Strchr(s string, c rune) int {
	for i, r := range s {
		if r == c {
			return i
		}
	}
	return -1
}

// Strrchr は文字列内で指定された文字を末尾から検索します
func Strrchr(s string, c rune) int {
	lastIndex := -1
	for i, r := range s {
		if r == c {
			lastIndex = i
		}
	}
	return lastIndex
}

// Strncmp は2つの文字列を最大n文字まで比較します
func Strncmp(s1, s2 string, n int) int {
	runes1 := []rune(s1)
	runes2 := []rune(s2)

	for i := 0; i < n; i++ {
		var r1, r2 rune
		if i < len(runes1) {
			r1 = runes1[i]
		}
		if i < len(runes2) {
			r2 = runes2[i]
		}

		if r1 != r2 {
			return int(r1) - int(r2)
		}

		// 両方とも終端に達した場合
		if i >= len(runes1) && i >= len(runes2) {
			return 0
		}
	}
	return 0
}

// Strdup は文字列を複製します
func Strdup(s string) string {
	runes := []rune(s)
	result := make([]rune, len(runes))
	for i, r := range runes {
		result[i] = r
	}
	return string(result)
}

// Substr は部分文字列を返します
func Substr(s string, start, length int) string {
	runes := []rune(s)

	if start < 0 || start >= len(runes) {
		return ""
	}

	end := start + length
	if end > len(runes) {
		end = len(runes)
	}

	return string(runes[start:end])
}

// Strjoin は2つの文字列を結合します
func Strjoin(s1, s2 string) string {
	runes1 := []rune(s1)
	runes2 := []rune(s2)
	result := make([]rune, len(runes1)+len(runes2))

	for i, r := range runes1 {
		result[i] = r
	}
	for i, r := range runes2 {
		result[len(runes1)+i] = r
	}

	return string(result)
}

// Strtrim は文字列の両端から指定された文字セットを削除します
func Strtrim(s string, set string) string {
	runes := []rune(s)
	setRunes := []rune(set)

	inSet := func(r rune) bool {
		for _, sr := range setRunes {
			if r == sr {
				return true
			}
		}
		return false
	}

	// 先頭から削除
	start := 0
	for start < len(runes) && inSet(runes[start]) {
		start++
	}

	// 末尾から削除
	end := len(runes)
	for end > start && inSet(runes[end-1]) {
		end--
	}

	return string(runes[start:end])
}

// Split は文字列を区切り文字で分割します
func Split(s string, sep rune) []string {
	if s == "" {
		return []string{""}
	}

	var result []string
	var current []rune

	for _, r := range s {
		if r == sep {
			result = append(result, string(current))
			current = nil
		} else {
			current = append(current, r)
		}
	}
	result = append(result, string(current))

	return result
}

// ============================================
// Part 3: 数値変換関数
// ============================================

// Atoi は文字列を整数に変換します
func Atoi(s string) int {
	runes := []rune(s)
	i := 0

	// 先頭の空白をスキップ
	for i < len(runes) && IsSpace(runes[i]) {
		i++
	}

	if i >= len(runes) {
		return 0
	}

	// 符号の処理
	sign := 1
	if runes[i] == '-' {
		sign = -1
		i++
	} else if runes[i] == '+' {
		i++
	}

	// 数値の変換
	result := 0
	for i < len(runes) && IsDigit(runes[i]) {
		digit := int(runes[i] - '0')
		result = result*10 + digit
		i++
	}

	return sign * result
}

// Itoa は整数を文字列に変換します
func Itoa(n int) string {
	if n == 0 {
		return "0"
	}

	negative := false
	if n < 0 {
		negative = true
		n = -n
	}

	var digits []rune
	for n > 0 {
		digit := rune(n%10 + '0')
		digits = append([]rune{digit}, digits...)
		n /= 10
	}

	if negative {
		digits = append([]rune{'-'}, digits...)
	}

	return string(digits)
}

// ============================================
// Part 4: メモリ操作関数
// ============================================

// Memset はバイトスライスを指定されたバイトで埋めます
func Memset(b []byte, c byte, n int) []byte {
	if b == nil || n <= 0 {
		return b
	}

	if n > len(b) {
		n = len(b)
	}

	for i := 0; i < n; i++ {
		b[i] = c
	}
	return b
}

// Memcpy はバイトスライスをコピーします
func Memcpy(dst, src []byte, n int) []byte {
	if dst == nil || src == nil || n <= 0 {
		return dst
	}

	if n > len(src) {
		n = len(src)
	}
	if n > len(dst) {
		n = len(dst)
	}

	for i := 0; i < n; i++ {
		dst[i] = src[i]
	}
	return dst
}

// Memmove はオーバーラップを考慮してバイトスライスをコピーします
func Memmove(dst, src []byte, n int) []byte {
	if dst == nil || src == nil || n <= 0 {
		return dst
	}

	if n > len(src) {
		n = len(src)
	}
	if n > len(dst) {
		n = len(dst)
	}

	// 一時バッファを使用してオーバーラップに対応
	temp := make([]byte, n)
	for i := 0; i < n; i++ {
		temp[i] = src[i]
	}
	for i := 0; i < n; i++ {
		dst[i] = temp[i]
	}

	return dst
}

// Memcmp は2つのバイトスライスを比較します
func Memcmp(s1, s2 []byte, n int) int {
	if n <= 0 {
		return 0
	}

	for i := 0; i < n; i++ {
		var b1, b2 byte
		if i < len(s1) {
			b1 = s1[i]
		}
		if i < len(s2) {
			b2 = s2[i]
		}

		if b1 != b2 {
			return int(b1) - int(b2)
		}
	}
	return 0
}

// Memchr はバイトスライス内で指定されたバイトを検索します
func Memchr(s []byte, c byte, n int) int {
	if s == nil || n <= 0 {
		return -1
	}

	if n > len(s) {
		n = len(s)
	}

	for i := 0; i < n; i++ {
		if s[i] == c {
			return i
		}
	}
	return -1
}

goft_test.go - テストコード

package goft

import "testing"

func TestIsAlpha(t *testing.T) {
	tests := []struct {
		input    rune
		expected bool
	}{
		{'a', true},
		{'z', true},
		{'A', true},
		{'Z', true},
		{'0', false},
		{' ', false},
		{'!', false},
	}

	for _, tt := range tests {
		result := IsAlpha(tt.input)
		if result != tt.expected {
			t.Errorf("IsAlpha(%q) = %v, want %v", tt.input, result, tt.expected)
		}
	}
}

func TestStrlen(t *testing.T) {
	tests := []struct {
		input    string
		expected int
	}{
		{"", 0},
		{"hello", 5},
		{"こんにちは", 5},
		{"hello世界", 7},
	}

	for _, tt := range tests {
		result := Strlen(tt.input)
		if result != tt.expected {
			t.Errorf("Strlen(%q) = %d, want %d", tt.input, result, tt.expected)
		}
	}
}

func TestAtoi(t *testing.T) {
	tests := []struct {
		input    string
		expected int
	}{
		{"0", 0},
		{"42", 42},
		{"-42", -42},
		{"  123", 123},
		{"+456", 456},
		{"123abc", 123},
		{"", 0},
	}

	for _, tt := range tests {
		result := Atoi(tt.input)
		if result != tt.expected {
			t.Errorf("Atoi(%q) = %d, want %d", tt.input, result, tt.expected)
		}
	}
}

func TestSplit(t *testing.T) {
	tests := []struct {
		input    string
		sep      rune
		expected []string
	}{
		{"a,b,c", ',', []string{"a", "b", "c"}},
		{"hello", ',', []string{"hello"}},
		{"", ',', []string{""}},
		{"a,,b", ',', []string{"a", "", "b"}},
	}

	for _, tt := range tests {
		result := Split(tt.input, tt.sep)
		if len(result) != len(tt.expected) {
			t.Errorf("Split(%q, %q) = %v, want %v", tt.input, tt.sep, result, tt.expected)
			continue
		}
		for i := range result {
			if result[i] != tt.expected[i] {
				t.Errorf("Split(%q, %q)[%d] = %q, want %q", tt.input, tt.sep, i, result[i], tt.expected[i])
			}
		}
	}
}