Go Conference 2019 Spring
BU(P$POGFSFODF4QSJOHFuzzy finder as a Go library
View Slide
XIPBNJw LUS Ωλϩʔɺ!LUS@w ৽ଔw (Pͱ$-*πʔϧ͕͖͢
'V[[ZpOEFSJTԿ
wೖྗʹର͠ɺ͍͋·͍ݕࡧ ʹΑͬͯΠϯλϥΫςΟϒʹ ಛఆͷߦΛબͰ͖ΔwίϚϯυϥΠϯGV[[ZpOEFS G[G G[Z FUD'V[[ZpOEFS
$ cd `ghq root`/`ghq list | fzf`HIRԼͷϦϙδτϦʹҠಈ
$ git branch | egrep -v '^\*' | fzf | xargs git checkoutΠϯλϥΫςΟϒʹνΣοΫΞτઌ ϒϥϯνΛબ
ίϚϯυϥΠϯGV[[ZpOEFSΛ ѻ͏ࡍʹൃੜ͢Δ
w ߦࢦͰ͋ΔͨΊɺؚΊΒΕΔใྔʹ੍ݶ͕͋Δw πʔϧʹΈࠐΉࡍͷෳࡶ͞ίϚϯυϥΠϯGV[[ZpOEFS Λѻ͏ࡍʹൃੜ͢Δ
w શ͘ಉ͡༰Λද͍ࣔͯ͠Δߦ͕ෳ͋Δͱ͖ɺ ϓϩάϥϜతɺϢʔβతʹͦΕΒΛ۠ผͰ͖ͳ͍w ใྔΛ૿͢͜ͱͰϢχʔΫʹͰ͖Δ͕ʜw ͱʹ͔͘ݟͮΒ͍w ิॿతͳใݕࡧରʹͳΓɺݕࡧਫ਼͕Լ͢Δߦࢦͷݶք
w DEίϚϯυͷ֦ுw ύεͷཤྺΛΠϯλϥΫςΟϒʹબͯ͠ҠಈCCSFOIBODE
w J5VOFTΛίϚϯυϥΠϯ͔Βૢ࡞Ͱ͖Δw J5VOFTͷۂΛΠϯλϥΫςΟϒʹબɺ࠶ੜLUSJUVOFTDMJ
w ίϚϯυϥΠϯGV[[ZpOEFSͷΠϯετʔϧw πʔϧʹೝࣝͤ͞ΔͨΊͷઃఆw ڥมͳͲπʔϧʹΈࠐΉࡍͷෳࡶ͞
'V[[ZpOEFSΛϥΠϒϥϦ ͱͯ͠ఏڙ͢Δ
w ߦࢦͰ͋ΔͨΊɺؚΊΒΕΔใྔʹ੍ݶ͕͋Δ‣ ෦తʹঢ়ଶΛ࣋ͭ͜ͱ͕Ͱ͖ɺϓϩάϥϜଆͰॏෳߦΛ۠ผͰ͖Δw πʔϧʹΈࠐΉࡍͷෳࡶ͞‣ (PʹͷΈґଘ͢ΔͨΊɺͦͦπʔϧͷઃఆ͕ෆཁ'V[[ZpOEFSBTBMJCSBSZ
LUSHPGV[[ZpOEFS
func Find(slice interface{},itemFunc func(i int) string,opts ...Option,) (idx int, err error)LUSHPGV[[ZpOEFS
EFNP
EFNPίϚϯυϥΠϯGV[[ZpOEFS
func main() {var slice []strings := bufio.NewScanner(os.Stdin)for s.Scan() {slice = append(slice, s.Text())}idx, err := fuzzyfinder.Find(slice, func(i int) string {return slice[i]})if err != nil {fmt.Fprintf(os.Stderr, "failed to find: %s\n", err)os.Exit(1)}fmt.Println(slice[idx])}
$ (cd $GOPATH/src/github.com/golang/go; git ls-files) | ./cli
EFNPಉҰ༰ͷߦͷදࣔ
w ϓϩάϥϜࣗॏෳߦͷผ͕Ͱ͖Δ͕ɺ ϢʔβવผͰ͖ͳ͍w ิॿతͳใΛϓϨϏϡʔΠϯυͰදࣔಉҰ༰ͷߦͷදࣔ
func main() {idx, err := fuzzyfinder.FindMulti(songs, func(i int) string {return songs[i].Title}, fuzzyfinder.WithPreviewWindow(func(i, w, h int) string {if i == -1 {return ""}return fmt.Sprintf("Title: %s\nArtist: %s\nAlbum: %s\n",songs[i].Title, songs[i].ArtistName, songs[i].AlbumName)}))if err != nil {fmt.Fprintf(os.Stderr, "failed to find: %s\n", err)os.Exit(1)}for _, i := range idx {fmt.Println(fmt.Sprintf("%s / %s / %s",songs[i].Title, songs[i].ArtistName, songs[i].AlbumName))}}
$ ./orange
HPGV[[ZpOEFSͷ࣮
w ೖྗͱީิߦͷϚονϯάw Ϛονͨ͠ߦͷείΞϦϯά'V[[ZpOEFSΛߏ͢Δཁૉ
w ೖྗlHSQDzΛؚΉߦΛ୳͢w ۪ʹೋॏϧʔϓճ͚ͩ͢Ϛονϯά
w ظ͍ͯ͠ΔߦΛ༏ઌతʹදࣔ͢ΔͨΊʹඞཁw ֤ߦΛιʔτ͢ΔͨΊͷείΞΛࢉग़͢ΔείΞϦϯά
w ϨʔϕϯγϡλΠϯڑw /FFEMFNBO8VOTDIw 4NJUI8BUFSNBO৭ʑͳείΞϦϯάΞϧΰϦζϜ
w ϨʔϕϯγϡλΠϯڑεϖϧνΣοΧʔw /FFEMFNBO8VOTDIG[Zw 4NJUI8BUFSNBOG[G৭ʑͳείΞϦϯάΞϧΰϦζϜ
w άϩʔόϧΞϥΠϯϝϯτΞϧΰϦζϜͷҰͭw ͋ΔͭͷจࣈྻΛྻͤ͞ΔͨΊʹ ඞཁͳίετΛಈతܭը๏ΛͬͯٻΊΔ/FFEMFNBO8VOTDIΞϧΰϦζϜ
/FFEMFNBO8VOTDIΞϧΰϦζϜ| A T A C-------------------------|A|C|
w ҎԼͷ͍ͣΕ͔ͷૢ࡞ͷ͏ͪɺ࠷είΞ͕ߴ͍ͷΛબw ۭന ΪϟοϓΛࠨͷจࣈྻʹҰͭૠೖw ۭന ΪϟοϓΛ্ͷจࣈྻʹҰͭૠೖw จࣈͱจࣈͷରԠ͚ ϚονPSϛεϚον/FFEMFNBO8VOTDIΞϧΰϦζϜ
w ҎԼͷ͍ͣΕ͔ͷૢ࡞ͷ͏ͪɺ࠷είΞ͕ߴ͍ͷΛબw ۭന ΪϟοϓΛࠨͷจࣈྻʹҰͭૠೖw ۭന ΪϟοϓΛ্ͷจࣈྻʹҰͭૠೖw จࣈͱจࣈͷରԠ͚ ϚονPSϛεϚονPS/FFEMFNBO8VOTDIΞϧΰϦζϜ
/FFEMFNBO8VOTDIΞϧΰϦζϜยํ͕ۭจࣈͷͱ͖ͷ είΞ࠷ॳʹٻ·Δ| A T A C-------------------------| 0 -2 -4 -6 -8A| -2C| -4
/FFEMFNBO8VOTDIΞϧΰϦζϜe.g. “ATAC” ʹରͯ͠ “” ΛΞϥΠϯϝϯτ͢Δʹ 4 ͭΪϟοϓΛૠೖ͢Εྑ͍| A T A C-------------------------| 0 -2 -4 -6 -8A| -2C| -4
/FFEMFNBO8VOTDIΞϧΰϦζϜ“” ͱ “” ʹͦΕͧΕจࣈΛରԠ͚Δ߹ɺϚον͍ͯ͠ΔͷͰ +2 (0 + 2 = 2)| A T A C-------------------------| 0 -2 -4 -6 -8A| -2C| -4
/FFEMFNBO8VOTDIΞϧΰϦζϜ“A” ͱ “” ͕͋Γɺ ޙऀʹΪϟοϓΛૠೖ ͢ΔͷͰ -2(-2 - 2 = -4)| A T A C-------------------------| 0 -2 -4 -6 -8A| -2C| -4
/FFEMFNBO8VOTDIΞϧΰϦζϜ“” ͱ “A” ͕͋Γɺ લऀʹΪϟοϓΛૠೖ ͢ΔͷͰ -2 (-2 - 2 = -4)| A T A C-------------------------| 0 -2 -4 -6 -8A| -2C| -4
/FFEMFNBO8VOTDIΞϧΰϦζϜmatch = 2left gap = -4top gap = -4 Ͳ͔͜ΒٻΊ͔ͨΛه| A T A C-------------------------| 0 -2 -4 -6 -8A| -2 2C| -4
/FFEMFNBO8VOTDIΞϧΰϦζϜmismatch = -3left gap = 0top gap = -6| A T A C-------------------------| 0 -2 -4 -6 -8A| -2 2 0C| -4
/FFEMFNBO8VOTDIΞϧΰϦζϜmatch = -2left gap = -2top gap = -8| A T A C-------------------------| 0 -2 -4 -6 -8A| -2 2 0 -2C| -4
/FFEMFNBO8VOTDIΞϧΰϦζϜmismatch = -7left gap = -4top gap = -10| A T A C-------------------------| 0 -2 -4 -6 -8A| -2 2 0 -2 -4C| -4
| A T A C-------------------------| 0 -2 -4 -6 -8A| -2 2 0 -2 -4C| -4 0 1 -1 0/FFEMFNBO8VOTDIΞϧΰϦζϜ࠷ऴతͳঢ়ଶ
| A T A C-------------------------| 0 -2 -4 -6 -8A| -2 2 0 -2 -4C| -4 0 1 -1 0/FFEMFNBO8VOTDIΞϧΰϦζϜඌͷείΞ͕ ͜ͷจࣈྻؒͷείΞ είΞ = 0
| A T A C-------------------------| 0 -2 -4 -6 -8A| -2 2 0 -2 -4C| -4 0 1 -1 0/FFEMFNBO8VOTDIΞϧΰϦζϜA T A C| |A - - Cඌ͔ΒҹͷॱʹḷΔ
w ϩʔΧϧΞϥΠϯϝϯτΞϧΰϦζϜͷҰͭw શମతʹࣅ͍ͯͳ͍ͭͷจࣈྻͷ ہॴతͳΞϥΠϯϝϯτΛٻΊΔΞϧΰϦζϜw /FFEMFNBO8VTDIͱඇৗʹࣅ͍ͯΔ͕ɺ είΞ͕ϚΠφεʹͳΒͳ͍4NJUI8BUFSNBOΞϧΰϦζϜ
4NJUI8BUFSNBOΞϧΰϦζϜw ҎԼͷ͍ͣΕ͔ͷૢ࡞ͷ͏ͪɺ࠷είΞ͕ߴ͍ͷΛબw ۭന ΪϟοϓΛࠨͷจࣈྻʹҰͭૠೖw ۭന ΪϟοϓΛ্ͷจࣈྻʹҰͭૠೖw จࣈͱจࣈͷରԠ͚ ϚονPSϛεϚονw θϩ
| A T A C-------------------------| 0 0 0 0 0A| 0 2 0 2 0C| 0 0 1 0 44NJUI8BUFSNBOΞϧΰϦζϜඌ͔Βઌ಄Ͱͳ͘ɺ෦తͳΞϥΠϯϝϯτ(ඌ͔Β 0 ·Ͱ)
| A T A C-------------------------| 0 0 0 0 0A| 0 2 0 2 0C| 0 0 1 0 44NJUI8BUFSNBOΞϧΰϦζϜείΞ = 4
| A T A C-------------------------| 0 0 0 0 0A| 0 2 0 2 0C| 0 0 1 0 44NJUI8BUFSNBOΞϧΰϦζϜA T A C| |- - A C
w ΪϟοϓΛ࡞Δ͜ͱࣗମͷϖφϧςΟw ઌ಄ҰகϘʔφεw FUDϘʔφεͱϖφϧςΟ
w ઃܭͦΕͧΕͷ࣮ʹΑΔw G[ZKIBXUIPSOG[Z"-(03*5).NEw G[GKVOFHVOOG[GTSDBMHPBMHPHPϘʔφεͱϖφϧςΟ
·ͱΊ
ೖྗʹϚον͢ΔߦΛߜΓࠐΉ 4NJUI8BUFSNBOͰ֤ߦΛείΞϦϯά είΞ͕ߴ͍ͷ͔ΒॱʹදࣔHPGV[[ZpOEFSͷ࣮
w ॏෳߦͷ۠ผw ϓϨϏϡʔΠϯυͰͷิॿతͳใͷ༩w πʔϧ͔ΒGV[[ZpOEFSΛͭ͘Δࡍʹɺ ίϚϯυϥΠϯGV[[ZpOEFSͷґଘΛղফͰ͖ΔHPGV[[ZpOEFSͷղܾ͢Δ͜ͱ
5IBOLTGPSMJTUFOJOH