【Golang】英単語の自動複数形変換をなんとか実現したい

2月 12, 2022

どもです。OFF-Whiteのジーンズ激カワ激高問題に苛まれるsaisaiです。


本日はGolangを使用して英単語の自動複数形変換をなんとか実現してみたのでコードを晒してみようと思います。


プログラミング言語学習歴2ヶ月目のがむしゃらコードだと思ってお手柔らかにみていただければと思います。


今回の成果物は以下の通りです。


それでは早速コードをご紹介させていただきたいと思います。

英単語の複数形パターン

まずは今回実装が必要な変換パターンを見ていきましょう。

①基本的には複数形のsを語尾につけます。
例)cat -> cats , dog -> dogs
②語尾がs/sh/ch/o/xの場合はesをつけます。
例)fish -> fishes , box -> boxes , tomato -> tomatoes
③語尾がf/feの場合はf/feをveに変換してsをつけます。
leaf -> leaves , knife -> knives
④語尾がyかつ語尾から2文字目が母音以外の場合はyをiに変えてes
boy -> boys , city -> cities

早速これらの変換を実現していきたいと思います。

まずは標準入力を受ける部分をさくっと実装。

 var a string
	fmt.Scan(&a)


①のパターンは他全ての条件に当てはまらないときに行われる処理としますので一旦置いておきます。まずは②の変換パターンを考えていきます。今回はこのような感じで書いてみました。それぞれのコードの意図もコメントしておきます

//該当するパターンを正規表現で定義します。今回は後ろ2文字を対象とするので以下のようにします。
re1 := regexp.MustCompile(`.s|sh|ch|.o|.x`) 

	v1 := a[len(a)-2 : len(a)-0] //標準入力された単語の後ろ2文字を変数v1に入れます。
	v2 := fmt.Sprint(re1.MatchString(v1)) //後ろ2文字と定義した正規表現がマッチするかの結果をv2に入れます
	if v2 == "true" {
		fmt.Printf("%ves\n", a)
		os.Exit(0)
	} //trueだった場合は標準入力された英単語にesを追加して出力、処理を終了します。

これでパターン②が実装できました。続いてパターン③です。

s1 := fmt.Sprint(strings.HasSuffix(a, "f")) //結果を変数に代入
	s2 := fmt.Sprint(strings.HasSuffix(a, "fe")) //結果を変数に代入

	if s1 == "true" && s2 == "false" {
		t := fmt.Sprintf("%s", strings.TrimRight(a, "f")) //文字列の右側からfをトリム
		fmt.Printf("%vves\n", t) //vesを追加して出力
		os.Exit(0) //処理を終了する
	} else if s1 == "false" && s2 == "true" {
		t := fmt.Sprintf("%s", strings.TrimRight(a, "fe")) //文字列の右側からfeをトリム
		fmt.Printf("%vves\n", t) //vesを追加して出力
		os.Exit(0) //処理を終了する
	}

strings.HasSuffixは語尾に特定の文字列を含むかどうかを判定します。今回はstringパッケージを幅広く使いたかったので別パターンのコードを使いましたが、ぶっちゃけパターン②と同じ方法でも実装できます。

続いて、パターン④です。パターン②を応用します。

re2 := regexp.MustCompile(`y`) //該当対象の文字列を正規表現で定義

e1 := a[len(a)-2 : len(a)-1] //末尾から2つ前の文字を変数に入れる
	e2 := a[len(a)-1:] //末尾の文字を変数に入れる
	e3 := fmt.Sprint(re2.MatchString(e2)) //定義している正規表現とマッチするかの結果を返す

	if e3 == "true" {
		if e1 != "a" && e1 != "i" && e1 != "u" && e1 != "e" && e1 != "o" {
    //母音ではないかどうかを判定(もっといい方法がある気がするなあ...)
			t := fmt.Sprintf("%s", strings.TrimRight(a, "y"))
    //末尾のyをトリム
			fmt.Printf("%vies\n", t) //末尾にiesを追加して出力します。
			os.Exit(0)
		}
	}

最後にどの条件にも該当しなかった場合の処理をひょっこり付け足します。

fmt.Printf("%vs\n", a)

これで一応一通りの処理を網羅しました。最終的には以下のような形になりました。

package main

import (
	"fmt"
	"os"
	"regexp"
	"strings"
)

func main() {
	var a string
	fmt.Scan(&a)

	re1 := regexp.MustCompile(`.s|sh|ch|.o|.x`)
	re2 := regexp.MustCompile(`y`)

	v1 := a[len(a)-2 : len(a)-0]
	v2 := fmt.Sprint(re1.MatchString(v1))
	if v2 == "true" {
		fmt.Printf("%ves\n", a)
		os.Exit(0)
	}

	s1 := fmt.Sprint(strings.HasSuffix(a, "f"))
	s2 := fmt.Sprint(strings.HasSuffix(a, "fe"))

	if s1 == "true" && s2 == "false" {
		t := fmt.Sprintf("%s", strings.TrimRight(a, "f"))
		fmt.Printf("%vves\n", t)
		os.Exit(0)
	} else if s1 == "false" && s2 == "true" {
		t := fmt.Sprintf("%s", strings.TrimRight(a, "fe"))
		fmt.Printf("%vves\n", t)
		os.Exit(0)
	}

	e1 := a[len(a)-2 : len(a)-1]
	e2 := a[len(a)-1:]
	e3 := fmt.Sprint(re2.MatchString(e2))

	if e3 == "true" {
		if e1 != "a" && e1 != "i" && e1 != "u" && e1 != "e" && e1 != "o" {
			t := fmt.Sprintf("%s", strings.TrimRight(a, "y"))
			fmt.Printf("%vies\n", t)
			os.Exit(0)
		}
	}
	fmt.Printf("%vs\n", a)
}

コードが少し長いか…?パッケージ分けした方が良かったかも…。

ひとこと

ということで今回は英単語複数形変換コードを考えてみました。


正直もっともっと簡素にできる箇所が山ほどありそうですが、作成したコードは記事にすることで学んだことを整理したいので勇気の記事化です。


もっといい方法などがあればどんどん教えていただきたいです。


ここまで読んでいただきありがとうございました!


-saisai-


↓本日のオススメ教材

Golang

Posted by CY