2

我需要根据字符向量模式验证字符串。我目前的代码是:

trim <- function (x) gsub("^\\s+|\\s+$", "", x)

# valid pattern is lowercase alphabet, '.', '!', and '?' AND
# the string length should be >= than 2
my.pattern = c(letters, '!', '.', '?')

check.pattern = function(word, min.size = 2)
{
    word = trim(word)
    chars = strsplit(word, NULL)[[1]]
    all(chars %in% my.pattern) && (length(chars) >= min.size)
}

例子:

w.valid = 'special!'
w.invalid = 'test-me'

check.pattern(w.valid) #TRUE
check.pattern(w.invalid) #FALSE

我想这非常慢......有没有更快的方法来做到这一点?也许正则表达式?谢谢!

PS:感谢大家的精彩回答。我的目标是构建一个 29 x 29 矩阵,其中行名和列名是允许的字符。然后我遍历一个巨大的文本文件的每个单词并构建一个“字母优先级”矩阵。例如,考虑从第一个字符开始的单词“special”:

row s, col p -> increment 1
row p, col e -> increment 1
row e, col c -> increment 1
... and so on.

我的代码的瓶颈是向量分配,我是在“追加”而不是预先分配最终的向量,所以代码需要 30 分钟来执行,而不是 20 秒!

4

2 回答 2

5

有一些内置函数可以清理您的代码。而且我认为您没有充分利用正则表达式的全部功能。

这里最突出的问题是strsplit。当你有正则表达式时,逐个字符比较事物的相等性是低效的。这里的模式使用方括号表示法来过滤你想要的字符。*用于任意数量的重复(包括零),而^and$符号表示行的开头和结尾,因此没有其他内容。nchar(word)是一样的length(chars)。更改&&&使函数向量化,以便您可以输入字符串向量并获得逻辑向量作为输出。

check.pattern.2 = function(word, min.size = 2)
{
    word = trim(word)
    grepl(paste0("^[a-z!.?]*$"),word) & nchar(word) >= min.size
}
check.pattern.2(c(" d ","!hello  ","nA!","  asdf.!"," d d "))
#[1] FALSE  TRUE FALSE  TRUE FALSE

接下来,使用花括号表示重复次数和 some paste0,该模式可以使用您的 min.size:

check.pattern.3 = function(word, min.size = 2)
{
    word = trim(word)
    grepl(paste0("^[a-z!.?]{",min.size,",}$"),word)
}
check.pattern.3(c(" d ","!hello  ","nA!","  asdf.!"," d d "))
#[1] FALSE  TRUE FALSE  TRUE FALSE

最后,您可以从以下位置内化正则表达式trim

check.pattern.4 = function(word, min.size = 2)
{
    grepl(paste0("^\\s*[a-z!.?]{",min.size,",}\\s*$"),word)
}
check.pattern.4(c(" d ","!hello  ","nA!","  asdf.!"," d d "))
#[1] FALSE  TRUE FALSE  TRUE FALSE
于 2013-01-31T19:11:40.423 回答
1

如果我正确理解你想要的模式,你会想要一个类似于格式的正则表达式:

^\\s*[a-z!\\.\\?]{MIN,MAX}\\s*$

其中 MIN 替换为字符串的最小长度,MAX 替换为字符串的最大长度。如果没有最大长度,则可以省略 MAX 和逗号。同样,如果 {} 中的所有内容(包括大括号本身)既没有最大值也没有最小值,则可以用 * 替换,这表示前面的项目将匹配零次或多次;这相当于 {0}。

这确保正则表达式仅匹配任何前导和尾随空格之后的每个字符都来自以下集合的字符串 * 小写字母 * 砰(感叹号) * 问号

请注意,这是用 Perl 风格的正则表达式编写的,因为它是我更熟悉的;我的大部分研究都是在这个 wiki for R text processing上进行的。

函数运行缓慢的原因是将字符串拆分为多个较小字符串的额外开销。与正则表达式(甚至是对字符串的手动迭代,比较每个字符直到到达末尾或找到无效字符)相比,这是很多开销。还要记住,该算法确保了 O(n) 的性能率,因为拆分会生成 n 个字符串。这意味着即使是 FAILING 字符串也必须至少执行 n 次操作来拒绝该字符串。

希望这可以阐明您遇到性能问题的原因。

于 2013-01-31T19:45:44.123 回答