NSLinguisticTagger
在语言学功能上来讲是一把名副其实的瑞士军刀,它可以讲自然语言的字符串标记为单词、确定词性和词根、划分出人名地名和组织名称、告诉你字符串使用的语言和语系。
对于我们大多数人来说,这其中蕴含着意义远超过我们所知道的,但或许也只是我们没有合适的机会使用而已。但是,几乎所有使用某种方式来处理自然语言的应用如果能够用上 NSLinguisticTagger
,或许就会润色不少,没准会催生一批新特性呢。
NSLinguisticTagger
和Siri同时出现于iOS 5上,所以可以推测这可能是苹果在私人助理方向开发时候的副产品。
回想一下我们经常问Siri的一个问题:
旧金山的天气怎么样?(What is the weather in San Francisco?)
电脑不可能通过逐字翻译"理解"问题的含义,不过我们耍一点儿小花招就可以合理地理解这个问题的_含义_:
NSString *question = @"What is the weather in San Francisco?";
NSLinguisticTaggerOptions options = NSLinguisticTaggerOmitWhitespace | NSLinguisticTaggerOmitPunctuation | NSLinguisticTaggerJoinNames;
NSLinguisticTagger *tagger = [[NSLinguisticTagger alloc] initWithTagSchemes: [NSLinguisticTagger availableTagSchemesForLanguage:@"en"] options:options];
tagger.string = question;
[tagger enumerateTagsInRange:NSMakeRange(0, [question length]) scheme:NSLinguisticTagSchemeNameTypeOrLexicalClass options:options usingBlock:^(NSString *tag, NSRange tokenRange, NSRange sentenceRange, BOOL *stop) {
NSString *token = [question substringWithRange:tokenRange];
NSLog(@"%@: %@", token, tag);
}];
输出如下:
What: Pronoun(代词) is: Verb(动词) the: Determiner(限定词) weather: Noun(名词) in: Preposition(介词) San Francisco: PlaceName(地名)
如果我们过滤名词、动词、地名,就会得到结果:[is, weather, San Francisco]
。
仅以来这个结果,或者配合潜在语义映射(Latent Semantic Mapping)库,我们就可以推断出合理解释,然后就可以通过调用相关API去获取旧金山此时此刻的天气状况了。
我们可以通过给 NSLinguisticTagger
设置下列scheme来标记不同类型的信息:
NSLinguisticTagSchemeTokenType
:将短语在大粒度上分成词语、标点符号、空格等。NSLinguisticTagSchemeLexicalClass
:将短语根据类型分为话语部分、标点空格等。NSLinguisticTagSchemeNameType
:将短语根据是否为命名实体分类。NSLinguisticTagSchemeNameTypeOrLexicalClass
:遵守 NSLinguisticTagSchemeNameType
对名字的规则和 NSLinguisticTagSchemeLexicalClass
对所有其它部分的原则。这里有一个不同短语类型和每一个分词方案之间关系的表:(NSLinguisticTagSchemeNameTypeOrLexicalClass
表示NSLinguisticTagSchemeNameType
和 NSLinguisticTagSchemeLexicalClass
的组合关系):
NSLinguisticTagSchemeTokenType | NSLinguisticTagSchemeLexicalClass | NSLinguisticTagSchemeNameType |
---|---|---|
|
|
|
用 NSLinguisticTagSchemeTokenType
来进行基本的分词(tokenization)就可以分辨出词语空格和标点符号了。至于话语信息或者区分话语的不同部分应该用 NSLinguisticTagSchemeLexicalClass
。
继续说其它scheme:
NSLinguisticTagSchemeLemma
: 在词根可知时可分析出词根。NSLinguisticTagSchemeLanguage
:根据短语的语言来标记。标记出的值会以标准语言所写形式给出,例如"en"
、"fr"
、"de"
等,和用 NSOrthography
类的效果相同。注意此类分词根据的是词语在整个句子或段落中的表意,而不是只根据该词本身来判断。NSLinguisticTagSchemeScript
:类似上述也是标记不同语言,但会以如下的缩略形式给出:"Latn"
、"Cyrl"
、"Jpan"
、"Hans"
、"Hant"
等。回头看上面给出的样例代码,首先用一个你想要用到的scheme组成的数组来初始化一个 NSLinguisticTagger
,然后在判断输入字符串的标记之后枚举出每一个tag。
除可用的标记scheme之外,还有一些可以传给 NSLinguisticTagger
的附加选项(用按位或|
)来改变细微的分词行为:
NSLinguisticTaggerOmitWords
NSLinguisticTaggerOmitPunctuation
NSLinguisticTaggerOmitWhitespace
NSLinguisticTaggerOmitOther
这些选项的每一个都可以细化标记所代表的广义类别。例如:NSLinguisticTagSchemeLexicalClass
配合 NSLinguisticTaggerOmitPunctuation
就可以在不同种类的标点符号中再细化。推荐用带block的迭代器或predicate来实现。
最后一个选项是针对 NSLinguisticTagSchemeNameType
的:
NSLinguisticTaggerJoinNames
默认一个名字中的每个短语都被分成不同的实例。很多情况下需要将类似“San Francisco”这样的名字当作一个短语而不是两个短语来看待。传入这个属性即可实现这个功能。
不幸的是在移动设备的UI设计中,自然语言处理并没有并没有被充分的利用。如果能够有效的利用,用户就可以用说话来代替手上的触摸动作来完成相同的事,而且会花费更少的时间。
当然要做到这点并不容易,但如果我们花费一点点时间能让应用在视觉上更赞,就可以给用户与设备和应用的交互体验上带来很大的颠覆。等到那时,再加上 NSLinguisticTagger
,使用移动应用将从未如此简单。
除非另有声明,本文采用知识共享「署名-非商业性使用 3.0 中国大陆」许可协议授权。