2015年4月18日土曜日

FabricでTwitter with Swift(iPhone)

Fabricを使って、Twitterクライアントを作る方法(Swift)



いろいろフレームワークがしてくれて楽なのですが、まだまだ未完成のようで、あまり機能がありません・・・
また、公式ドキュメントも一部Objective-Cしかなく、頼りないです。

ただし、セルの高さの計算・アイコン画像の非同期受信などはすべて自動で行ってくれます。ログインボタンもとても簡単に実装することができます。


Qiitaー[iOS]FabricでTwitterクライアントを作った
このページがとてもわかり易いです。Fabricの適用方法などはこちらを参照してください。

このページのTwitterAPI.swiftを拡張して、サーチ・投稿・IDによるツイートの取得の関数を追加しました。

GitHub上に公開しています。
https://github.com/ha1fha1f/Lit_final/blob/master/TwitterAPI.swift

以下、上のAPIを用いたデータの保持・取得を行うクラスの例です。(2015/4/17現在)

import Foundation
import TwitterKit

class TweetDataModel :NSObject{
    
    var tweets:[TWTRTweet]
    
    // initialize
    override init() {
        self.tweets = []
    }
    
    func fetchTimeline(maxid:String?){
        TwitterAPI.getHomeTimeline({
            twttrs in
            
            println("fetch")
            
            if self.tweets.count == 0 {
                self.tweets = twttrs
            }else{
                let tmptweets:[TWTRTweet]
                
                if twttrs[0].tweetID > self.tweets.last?.tweetID {//新しいツイートがある
                    tmptweets = reverse(twttrs)
                }else{
                    //if twttrs.last?.tweetID < self.tweets[0].tweetID{//古いツイートがある
                    tmptweets=twttrs
                }
            
                for tweetCell in tmptweets {
                    if tweetCell.tweetID > self.tweets[0].tweetID {//新ツイートを上に追加
                        self.tweets.insert(tweetCell,atIndex: 0)
                    }else if tweetCell.tweetID < self.tweets.last?.tweetID {//古いツイートを下に追加
                        self.tweets.append(tweetCell)
                    }
                }
            }
            println("finish")

            //notificationを送る
            NSNotificationCenter.defaultCenter().postNotificationName("tweetLoaded", object: nil)
            
            },
            maxid: maxid,
            count: "40",
            error: {error in println(error.localizedDescription)})
    }
}
ご参考までに。
これは.Darkのテーマでの表示です!

セルのテーマカラーは二種類用意されています。.Darkと.Lightです。

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier("cell") as! OriginalTweetTableViewCell
        
        println("\(indexPath.row)")
        
        if tweetData.tweets.count > indexPath.row {
            let tweet = tweetData.tweets[indexPath.row]
            cell.tag = indexPath.row
            cell.configureWithTweet(tweet)
            cell.tweetView.theme = .Dark
            cell.delegate = self
        }
        
        return cell
    }
のように、セルを返すときに、tweetView.themeを設定すればよいです。

僕の場合はTWTRTweetCellを継承したオリジナルのクラスを作ったのでやや異なりますが、設定方法などはだいたい同じです。
(https://github.com/ha1fha1f/Lit_final/blob/master/Litfinal/OriginalCell.swift)

雑になってしまいましたが、終わります

2015年4月17日金曜日

Swift(iPhone)での形態素解析〜NSLinguisticTagger〜

iPhoneにmecabのframeworkが搭載されているのは見えるが、公式のリファレンスがなく、使用することができない。

しかし、それ以外の(?)形態素解析を用いることができる。
iOS 5.1から搭載されているが、紹介記事が少ないので。

        var testString="iPhoneでも形態素解析を手軽に、だれでも行うことができます。ただし、日本語の場合、機能制限が多いです"
        let tagschemes = NSLinguisticTagger.availableTagSchemesForLanguage("ja")//取得するだけ
        let tagger = NSLinguisticTagger(tagSchemes: tagschemes,options: 0)//インスタンス作成
        let options: NSLinguisticTaggerOptions = .OmitWhitespace | .OmitPunctuation | .JoinNames
        tagger.string=testString//文字列をセット
        tagger.enumerateTagsInRange(NSMakeRange(0, (testString as NSString).length), scheme: NSLinguisticTagSchemeTokenType, options: options) {
            (tag, tokenRange, sentenceRange, _) in
            let token = (self.testString as NSString).substringWithRange(tokenRange)
            println("\(token): \(tag)")
        }
たったこれだけ。

NSLinguisticTagger.availableTagSchemesForLanguage で取得できるのは、使える機能のリストです。
日本語("ja")では[TokenType, Language, Script]
英語("en")では[TokenType,Language,Script,Lemma,LexicalClass,NameType,NameTypeOrLexicalClass]
が返ってきます。

一般的な形態素解析(品詞まで分解)はNSLinguisticTagSchemeLexicalClassです

日本語に対してできる最大限、NSLinguisticTagSchemeTokenTypeを行うと、文をトークンに分割したものを出力してくれます。"tag"は基本的に"Word"になっています。

何かに使えればいいな。

ただし、認識精度は非常に低いです。。。
token= 今日: Word
token= : Word
token= : Word
token= : Word
token= 天気: Word
token= です: Word
token= : Word
token= なんで: Word
token= : Word
token= : Word
token= : Word
token= 同じ: Word
token= 単語: Word
token= : Word
token= : Word
token= : Word
token= 認識: Word
token= : Word
token= : Word
token= ない: Word
token= : Word
token= じゃ: Word

mecabなら
今日はいい天気ですね
今日 名詞,副詞可能,*,*,*,*,今日,キョウ,キョー
助詞,係助詞,*,*,*,*,は,ハ,ワ
いい 形容詞,自立,*,*,形容詞・イイ,基本形,いい,イイ,イイ
天気 名詞,一般,*,*,*,*,天気,テンキ,テンキ
です 助動詞,*,*,*,特殊・デス,基本形,です,デス,デス
助詞,終助詞,*,*,*,*,ね,ネ,ネ
EOS
なんで「いい」が同じ単語として認識されないんじゃ
なんで 副詞,一般,*,*,*,*,なんで,ナンデ,ナンデ
記号,括弧開,*,*,*,*,「,「,「
いい 形容詞,自立,*,*,形容詞・イイ,基本形,いい,イイ,イイ
記号,括弧閉,*,*,*,*,」,」,」
助詞,格助詞,一般,*,*,*,が,ガ,ガ
同じ 連体詞,*,*,*,*,*,同じ,オナジ,オナジ
単語 名詞,一般,*,*,*,*,単語,タンゴ,タンゴ
として 助詞,格助詞,連語,*,*,*,として,トシテ,トシテ
認識 名詞,サ変接続,*,*,*,*,認識,ニンシキ,ニンシキ
動詞,自立,*,*,サ変・スル,未然レル接続,する,サ,サ
動詞,接尾,*,*,一段,未然形,れる,レ,レ
ない 助動詞,*,*,*,特殊・ナイ,基本形,ない,ナイ,ナイ
名詞,非自立,一般,*,*,*,ん,ン,ン
じゃ 助詞,副助詞,*,*,*,*,じゃ,ジャ,ジャ

EOS




参考URL:
http://nshipster.com/nslinguistictagger/
http://d.hatena.ne.jp/shu223/20130318/1363566717

2015年4月12日日曜日

Google Code Jam-Qualification Round 2015

昨日、行われていたので、とりあえず飛び入り参加しました!
Google Code Jam-Qualification Round 2015

ルール

  • 問題は全部で四問ある。4問それぞれにSmallデータセット、Largeデータセットの2つがあり、それぞれに配点がある。満点は100。
  • 同じ点数なら、最初に提出してからの時間+ペナルティ時間が短いほうの勝利。
  • 27時間の間にとくプログラムを作成する。
  • 各問題について、まずSmallのデータセットをダウンロードして、3分以内にそのデータセットに対する回答と、とくのに使ったプログラムを提出する。"InCorrect"に対しては4分のペナルティが課される。回答が"Rejected"(形式が違う)された場合は、再度提出できる。それ以外の場合、再提出はできない。
  • Smallのデータセットに対して"Correct"判定をもらうと、Largeのデータセットにチャレンジすることができる。チャンスは1回のみ、ダウンロードしてから8分以内に回答を提出できなければ"InCorrect"となる。ただし、8分の間は何度でも提出し直すことができるが、最後に提出したもののみ採点対象となる。得点は一時的に追加されるが、コンテスト終了後に採点される。
  • 20点以上獲得すればQualification Round突破

僕は、A-SL, B-SL, C-Sだけ解けました
C-Lも解けたのですが、対応に間に合わず、8分の間には提出できませんでした・・・

D問題、たまたま先日悩んで諦めた課題だったので、諦めました
代わりに(?)、こんなリンクを貼っておきます


以下、僕の回答です。

A問題:Standing Ovation

#coding:utf-8

fr=open("A-large.in","r")

output=""
ans=[]
casenum=0

count=0
for line in fr:
    if count==0:
        casenum=int(line)
    else:
        tmp=line.split()
        Smax=int(tmp[0])
        sumnum=0
        level=0
        tmpans=0
        for a in list(tmp[1]):
            number=int(a)
            lack=level-sumnum
            if lack <= 0:#ok
                sumnum=sumnum+number
            else:
                tmpans=tmpans+lack
                sumnum=sumnum+number+lack
            level=level+1
        ans.append(tmpans)
    count=count+1



for i in range(0,casenum):
    output=output+"Case #"+str(i+1)+": "+str(ans[i])+"\n"

fr.close()


fw=open("out.txt","w")
fw.write(output)
fw.close()




  • 前から順にシミュレーションして、足りない場合は不足としてカウントしました。

  • B問題:Infinite House of Pancakes

    #coding:utf-8
    
    #D人の客が、それぞれPi枚のパンケーキを持っている
    #他の客は何もない
    #一分に一枚食べる
    #specialtimeは誰も食べない、パンケーキ有りの客からその他の客へパンケーキが移る
    #できるだけ早くパンケーキをなくす
    
    
    #kに展開した時のかかる時間数
    simulate=lambda pan,k :sum([int((i-1)/k) for i in pan])+k
    
    fr=open("B-large.in","r")
    
    output=""
    ans=[]
    casenum=0
    
    count=0
    for line in fr:
        if count==0:
            casenum=int(line)
        else:
            if (count%2) == 0:#list
                tmpans=10000
                diners=list(map(int,line.split()))
                for i in range(1,max(diners)+1):
                    tmp=simulate(diners,i)
                    if tmpans>tmp:
                        tmpans=tmp
                ans.append(tmpans)
        count=count+1
    
    
    for i in range(0,casenum):
        output=output+"Case #"+str(i+1)+": "+str(ans[i])+"\n"
    
    fr.close()
    
    
    fw=open("out.txt","w")
    fw.write(output)
    fw.close()
    





  • まず気づかなければならないのは、できるだけ早く分散させておいたほうがいいこと。
  • kこづつに分散させるのに、どのkが最適かわからなかったので、各kに対して回数を計算し、最小のものを選択しました。これでも時間的には大丈夫です。

  • C問題:Dijkstra

    #coding:utf-8
    #四元数
    
    
    def multi(b,a):#a*b
        dic={"11":"1", "1i":"i", "1j":"j", "1k":"k","i1":"i", "ii":"-1","ij":"-k", "ik":"j","j1":"j", "ji":"k", "jj":"-1", "jk":"-i","k1":"k", "ki":"-j", "kj":"i", "kk":"-1"}
        sign=["","-"]
        signflag=0
        if a[0]=="-":
            signflag=signflag+1
            a=a[1]
        if b[0]=="-":
            signflag=signflag+1
            b=b[1]
        res=dic[a+b]
        if res[0]=="-":
            signflag=signflag+1
            res=res[1]
        return sign[signflag%2] + res
    
    def convert(tmpstring):
        tmpc=tmpstring[0]
        for c in tmpstring[1:]:
            tmpc=multi(tmpc,c)
        return tmpc
    
    def main():
        fr=open("C-large.in","r")
        output=""
        ans=[]
        casenum=0
        count=0
        for line in fr:
            if count==0:
                casenum=int(line)
            else:
                if (count%2) == 1:#繰り返し回数
                    repeatnum=int(line.split()[1])
                else:
                    print(len(ans))
                    tmpans=False
                    stringdata=line.replace("\n","")
                    rank=0
                    tmp="1"
                    converted=convert(stringdata)
                    for j in range(0,repeatnum):
                        #あとは残りがkであることを示すのみ
                        if rank == 2:
                            for fds in range(0,((repeatnum-j)%4)):
                                tmp=multi(tmp,converted)
                            break
                        #i,jすら見つからないのであきらめ
                        elif j>=9:
                           break
                        #とりまi,jを探しに行く
                        else:
                            for c in stringdata[0:]:
                                tmp=multi(tmp,c)
                                #ひとまずiを見つける、あればjを探す、
                                if rank==0:
                                    if tmp=="i":
                                        tmp="1"
                                        rank=1
                                elif rank==1:
                                    if tmp=="j":
                                        tmp="1"
                                        rank=2
    
                    if rank==2 and tmp=="k":
                        tmpans=True
    
                    if tmpans:
                        ans.append("YES")
                    else:
                        ans.append("NO")
            count=count+1
    
        for i in range(0,casenum):
            output=output+"Case #"+str(i+1)+": "+str(ans[i])+"\n"
    
        fr.close()
    
    
        fw=open("out.txt","w")
        fw.write(output)
        fw.close()
    
    main()
    • まず気づかなければならないのは、"1"と等価な部分は右に含めても左に含めてもいいこと。つまり、"ijk"と等価なら、"i111jk"と等価なとき、当然"i11jk"、"i1jk","ijk"と等価なので、左から順にiを見つけ、jを見つけ、残りがkであればよい。
    •  large突破のポイントはループ対策。1111もiiiiもkkkkもjjjjも、すべて"1"と等価。より、n回同じものをかける場合は、n%4回かけるだけでよい
    • Cython使おうとしましたがあまり変わりませんでした。

    以上です。