2010年11月15日月曜日

GDD 2010 dev quizのしりとりを解く(はずの)Goコード

ちょっと前GDD 2010のdev quiz用に書いたGoのコードを公開してみる。当日は出張で出席できないことがわかってたので、実際に申し込んではいないからあってるかどうかは定かでない。とりあえずLevel 2まではそれっぽい答が出てるのは確認した。Level 3は時間がかかりすぎるパスを先に切らないと常識的な時間で終わらなかったはず。

ちなみに、元々はPythonで書いたのをGoに書き直したコードだったりする。実行時間的には4分かかってた処理が20秒くらいに縮まった。

コード


package main
import (
    "flag"
    "os"
    "log"
    "bufio"
    "fmt"
    "strings"
    "container/vector"
)

var logTag string = ""
var logger *log.Logger = log.New(os.Stdout, logTag, 0)

func ExtractFirstAndLastLetter(word string) string {
    first := word[0]
    last := word[len(word)-1]
    return fmt.Sprintf("%c%c", first, last)
}

var CHAR_LIST string = "abcdefghijklmnopqrstuvwxyz"

type StringList struct {
    list vector.StringVector
}

func (l *StringList) add(s string) {
    l.list.Push(s)
}

func (l *StringList) copy() StringList {
    copied := l.list.Copy()
    return StringList { copied }
}

func (l *StringList) delete(index int) {
    l.list.Delete(index)
}

func (l *StringList) size() int {
    return l.list.Len()
}


func (l *StringList) join(sep string) string {
    return strings.Join(l.list, sep)
}

type WordDictionary map[byte] StringList

func (d WordDictionary) copy() WordDictionary {
    newDict := make(WordDictionary)
    for key,val := range d {
        newDict[key] = val.copy()
    }
    return newDict
}

const (
    WIN = 0
    LOSE = 1
)

func DoShiritoriToEnd(dict WordDictionary, word string, wordPath StringList, depth int) {
    depth += 1
    endIndex := len(word)-1
    lastChar := word[endIndex]
    wordList := dict[lastChar]
    if wordList.size() == 0 {
        joined := wordPath.join("-")
        winLose := depth % 2
        if winLose == WIN {
            logger.Print(" WIN:"+joined)
        } else {
            logger.Print("LOSE:"+joined)
        }
    }

    for i, nextWord := range wordList.list {
        copiedDict := dict.copy()
        copiedList := copiedDict[lastChar]
        copiedPath := wordPath.copy()
        copiedPath.add(nextWord)
        copiedList.delete(i)
        copiedDict[lastChar] = copiedList
        DoShiritoriToEnd(copiedDict, nextWord, copiedPath, depth)
    }
}
func main() {
    inFilePath := flag.String("in","","input data file")
    flag.Parse()

    if *inFilePath == "" {
        logger.Print("Input file not spepcified")
        return
    }

    // open input and output files
    logger.Printf("Input file: %s\n", *inFilePath)

    inFile,err := os.Open(*inFilePath, os.O_RDONLY, 0)
    if err != nil {
        logger.Print(err)
        return
    }

    defer func() {
        inFile.Close()
    }()

    // get the first word
    reader := bufio.NewReader(inFile)
    line, err := reader.ReadString('\n')
    if err != nil {
        logger.Print(err)
        return
    }

    firstWord := ExtractFirstAndLastLetter(line[0:len(line)-1])
    startDict := make(WordDictionary, len(CHAR_LIST))
    for {
        line, err = reader.ReadString('\n')
        if err != nil {
            //logger.Print("Error while reading file")
            break
        }

        if len(line) == 1 {
            continue
        }

        word := line[0:len(line)-1]
        word = ExtractFirstAndLastLetter(word)
        //logger.Print(word)
        firstLetter := word[0]
        list := startDict[firstLetter]
        list.add(word)
        startDict[firstLetter] = list
    }

    //logger.Print(startDict)
    var list StringList
    DoShiritoriToEnd(startDict, firstWord, list, 0)
}

入力(level1.txt)

xrdjgorj
jctbqs
jkrshfcilv
jorsezhajk
jmuholsxrc
sfdpdioerpv
silpaavk
vtvauxgju
vogmqa
vnofdnnc
vdruy
uemxza
udbjf
uvtpf
aszhnuvn
awmhkgyd
alhjlmioar
ngzmjyd
nznxgp
dvvcghudkww
dwivxpujzdo
deqwbye
wotaaadgpo
wgaiqosarg
wyeflkkwxg
wzltrkb
oqyjlgapkih
oeqgb
hwfnhx
kcofl
lubhgkf
fywggvxnixm
mxmlqukzcp
pfliwzwot
pzgilzqvwr
teytvanbrsw
tonhvlvg
gnsxomhwdz
zsxyei
ihcsq
ckxuhuty
ydlwmlwskf
rkaqnmnb
btjryytmvye
ezdfvkx
xmzxpvq

実行結果

$ ./shiritori -in=level1.txt
Input file: level1.txt
 WIN:js-sv-vu-ua-an-nd-dw-wo-oh-hx-xq
LOSE:js-sv-vu-ua-an-nd-dw-wo-ob-be-ex-xq
 WIN:js-sv-vu-ua-an-nd-dw-wg-gz-zi-iq
 WIN:js-sv-vu-ua-an-nd-dw-wg-gz-zi-iq
 WIN:js-sv-vu-ua-an-nd-dw-wb-be-ex-xq
LOSE:js-sv-vu-ua-an-nd-do-oh-hx-xq
(後略)

2010年11月12日金曜日

Goの仕様変更

Go一周年ということで久しぶりに最新版にしたところ、コンパイルエラーが発生したのでその原因を探してみた。結果、いくつか仕様に変更があったことが判明。自分に影響があったのは以下の2つ。

  • 2010-10-13のリリースでインターフェースへのポインタが自動的に参照されなくなった
  • 2010-10-20のリリースでlogパッケージのインターフェースが変更になった

インターフェースへのポインタの扱いが変更

リリースノートの該当箇所は以下。

The language change is that uses of pointers to interface values no longer
automatically dereference the pointer.  A pointer to an interface value is more
often a beginner’s bug than correct code.

ようするにインタフェースのポインタとか使うなよ!という変更点。自分のコードでは次のように変える必要があった。ちなみに変更前のコードではconn.Closeでコンパイルエラー(undefinedエラー)が起きてた。

変更前

func communicate(conn *net.Conn) {
    defer func() {
        conn.Close()
    }()
(以下略)

変更後

func communicate(conn net.Conn) {
    defer func() {
        conn.Close()
    }()
(以下略)

logパッケージのインタフェースが変更

個人的に結構影響のあった変更。関数名やらが変わったので色々置換する必要があった。

変更前

var logger *log.Logger = log.New(os.Stdout, nil , "", log.Lok)
logger.Log("Error")

変更後

var logger *log.Logger = log.New(os.Stdout, "", 0)
logger.Print("Error")