以编程方式确定是否用“a”或“an”描述对象?

时间:2010-12-29 22:29:50

标签: php grammar

我有一个名词数据库(ex“house”,“感叹号”,“苹果”),我需要在我的应用程序中输出和描述。很难用一个自然的句子来描述一个项目而不使用“a”或“an” - “房子很大”,“感叹号很小”等等。

我是否可以在PHP中使用任何函数,库或hack来确定用A或AN描述任何给定名词是否更合适?

8 个答案:

答案 0 :(得分:8)

我需要这个用于C#项目,所以这里是上面提到的Python code的C#端口。确保在源文件中包含using System.Text.RegularExpressions;

private string GetIndefiniteArticle(string noun_phrase)
{
    string word = null;
    var m = Regex.Match(noun_phrase, @"\w+");
    if (m.Success)
        word = m.Groups[0].Value;
    else
        return "an";

    var wordi = word.ToLower();
    foreach (string anword in new string[] { "euler", "heir", "honest", "hono" })
        if (wordi.StartsWith(anword))
            return "an";

    if (wordi.StartsWith("hour") && !wordi.StartsWith("houri"))
        return "an";

    var char_list = new char[] { 'a', 'e', 'd', 'h', 'i', 'l', 'm', 'n', 'o', 'r', 's', 'x' };
    if (wordi.Length == 1)
    {
        if (wordi.IndexOfAny(char_list) == 0)
            return "an";
        else
            return "a";
    }

    if (Regex.Match(word, "(?!FJO|[HLMNS]Y.|RY[EO]|SQU|(F[LR]?|[HL]|MN?|N|RH?|S[CHKLMNPTVW]?|X(YL)?)[AEIOU])[FHLMNRSX][A-Z]").Success)
        return "an";

    foreach (string regex in new string[] { "^e[uw]", "^onc?e\b", "^uni([^nmd]|mo)", "^u[bcfhjkqrst][aeiou]" })
    {
        if (Regex.IsMatch(wordi, regex))
            return "a";
    }

    if (Regex.IsMatch(word, "^U[NK][AIEO]"))
        return "a";
    else if (word == word.ToUpper())
    {
        if (wordi.IndexOfAny(char_list) == 0)
            return "an";
        else
            return "a";
    }

    if (wordi.IndexOfAny(new char[] { 'a', 'e', 'i', 'o', 'u' }) == 0)
        return "an";

    if (Regex.IsMatch(wordi, "^y(b[lor]|cl[ea]|fere|gg|p[ios]|rou|tt)"))
        return "an";

    return "a";
}

答案 1 :(得分:7)

我也在寻找这样的解决方案,但是在JavaScript中。所以我将它移植到JS,您可以在github https://github.com/rigoneri/indefinite-article.js

中查看实际项目

以下是代码段:

 function indefinite_article(phrase) {

    // Getting the first word 
    var match = /\w+/.exec(phrase);
    if (match)
        var word = match[0];
    else
        return "an";

    var l_word = word.toLowerCase();
    // Specific start of words that should be preceeded by 'an'
    var alt_cases = ["honest", "hour", "hono"];
    for (var i in alt_cases) {
        if (l_word.indexOf(alt_cases[i]) == 0)
            return "an";
    }

    // Single letter word which should be preceeded by 'an'
    if (l_word.length == 1) {
        if ("aedhilmnorsx".indexOf(l_word) >= 0)
            return "an";
        else
            return "a";
    }

    // Capital words which should likely be preceeded by 'an'
    if (word.match(/(?!FJO|[HLMNS]Y.|RY[EO]|SQU|(F[LR]?|[HL]|MN?|N|RH?|S[CHKLMNPTVW]?|X(YL)?)[AEIOU])[FHLMNRSX][A-Z]/)) {
        return "an";
    }

    // Special cases where a word that begins with a vowel should be preceeded by 'a'
    regexes = [/^e[uw]/, /^onc?e\b/, /^uni([^nmd]|mo)/, /^u[bcfhjkqrst][aeiou]/]
    for (var i in regexes) {
        if (l_word.match(regexes[i]))
            return "a"
    }

    // Special capital words (UK, UN)
    if (word.match(/^U[NK][AIEO]/)) {
        return "a";
    }
    else if (word == word.toUpperCase()) {
        if ("aedhilmnorsx".indexOf(l_word[0]) >= 0)
            return "an";
        else 
            return "a";
    }

    // Basic method of words that begin with a vowel being preceeded by 'an'
    if ("aeiou".indexOf(l_word[0]) >= 0)
        return "an";

    // Instances where y follwed by specific letters is preceeded by 'an'
    if (l_word.match(/^y(b[lor]|cl[ea]|fere|gg|p[ios]|rou|tt)/))
        return "an";

    return "a";
}

答案 2 :(得分:6)

您想要的是确定适当的不定冠词。 Lingua::EN::Inflect是一个很棒的Perl模块。我已经提取了相关代码并将其粘贴在下面。它只是一堆案例和一些正则表达式,因此移植到PHP应该不难。如果有人有兴趣,朋友将其移植到Python here

# 2. INDEFINITE ARTICLES

# THIS PATTERN MATCHES STRINGS OF CAPITALS STARTING WITH A "VOWEL-SOUND"
# CONSONANT FOLLOWED BY ANOTHER CONSONANT, AND WHICH ARE NOT LIKELY
# TO BE REAL WORDS (OH, ALL RIGHT THEN, IT'S JUST MAGIC!)

my $A_abbrev = q{
(?! FJO | [HLMNS]Y.  | RY[EO] | SQU
  | ( F[LR]? | [HL] | MN? | N | RH? | S[CHKLMNPTVW]? | X(YL)?) [AEIOU])
[FHLMNRSX][A-Z]
};

# THIS PATTERN CODES THE BEGINNINGS OF ALL ENGLISH WORDS BEGINING WITH A
# 'y' FOLLOWED BY A CONSONANT. ANY OTHER Y-CONSONANT PREFIX THEREFORE
# IMPLIES AN ABBREVIATION.

my $A_y_cons = 'y(b[lor]|cl[ea]|fere|gg|p[ios]|rou|tt)';

# EXCEPTIONS TO EXCEPTIONS

my $A_explicit_an = enclose join '|',
(
    "euler",
    "hour(?!i)", "heir", "honest", "hono",
);

my $A_ordinal_an = enclose join '|',
(
    "[aefhilmnorsx]-?th",
);

my $A_ordinal_a = enclose join '|',
(
    "[bcdgjkpqtuvwyz]-?th",
);

sub A {
    my ($str, $count) = @_;
    my ($pre, $word, $post) = ( $str =~ m/\A(\s*)(?:an?\s+)?(.+?)(\s*)\Z/i );
    return $str unless $word;
    my $result = _indef_article($word,$count);
    return $pre.$result.$post;
}

sub AN { goto &A }

sub _indef_article {
    my ( $word, $count ) = @_;

    $count = $persistent_count
        if !defined($count) && defined($persistent_count);

    return "$count $word"
        if defined $count && $count!~/^($PL_count_one)$/io;

    # HANDLE USER-DEFINED VARIANTS

    my $value;
    return "$value $word"
        if defined($value = ud_match($word, @A_a_user_defined));

    # HANDLE ORDINAL FORMS

    $word =~ /^($A_ordinal_a)/i         and return "a $word";
    $word =~ /^($A_ordinal_an)/i        and return "an $word";

    # HANDLE SPECIAL CASES

    $word =~ /^($A_explicit_an)/i       and return "an $word";
    $word =~ /^[aefhilmnorsx]$/i        and return "an $word";
    $word =~ /^[bcdgjkpqtuvwyz]$/i      and return "a $word";


    # HANDLE ABBREVIATIONS

    $word =~ /^($A_abbrev)/ox           and return "an $word";
    $word =~ /^[aefhilmnorsx][.-]/i     and return "an $word";
    $word =~ /^[a-z][.-]/i              and return "a $word";

    # HANDLE CONSONANTS

    $word =~ /^[^aeiouy]/i              and return "a $word";

    # HANDLE SPECIAL VOWEL-FORMS

    $word =~ /^e[uw]/i                  and return "a $word";
    $word =~ /^onc?e\b/i                and return "a $word";
    $word =~ /^uni([^nmd]|mo)/i         and return "a $word";
    $word =~ /^ut[th]/i                 and return "an $word";
    $word =~ /^u[bcfhjkqrst][aeiou]/i   and return "a $word";

    # HANDLE SPECIAL CAPITALS

    $word =~ /^U[NK][AIEO]?/            and return "a $word";

    # HANDLE VOWELS

    $word =~ /^[aeiou]/i                and return "an $word";

    # HANDLE y... (BEFORE CERTAIN CONSONANTS IMPLIES (UNNATURALIZED) "i.." SOUND)

    $word =~ /^($A_y_cons)/io           and return "an $word";

    # OTHERWISE, GUESS "a"
    return "a $word";
}

答案 3 :(得分:0)

制作一个带元音的数组。检查您正在检查的单词的第一个字母是否在元音数组中。除了处理首字母缩略词时,它会起作用。

答案 4 :(得分:0)

从头开始编写应该很容易,tbh。如果一个单词以元音开头,则会得到'a';如果它以辅音开头,则会得到'an'。以编程方式,这很容易 - 如果你有任何边缘情况(例如你可能使用BBC英式风格'一个历史性的场合')你可以单独处理它们。

有点像使用变形器,只有'a'/'an'语法规则而不是复数。看看CakePHP或Rails如何处理变形,以便更全面地讨论这个概念,包括如何处理边缘情况 - 例如,你不想将'鹿'变成复数形式的'鹿'或'鹅' 'gooses',所以他们需要单独处理,就像你自己的边缘情况,如'宇宙'或吸气/非吸气'H'。

答案 5 :(得分:0)

正在寻找这样的解决方案,谢谢marcog。这是尝试移植你朋友的python版本(我不知道python或perl,所以可能有一些错误):

function indefinite_article($word) {
    // Lowercase version of the word
    $word_lower = strtolower($word);

    // An 'an' word (specific start of words that should be preceeded by 'an')
    $an_words = array('euler', 'heir', 'honest', 'hono');
    foreach($an_words as $an_word) {
            if(substr($word_lower,0,strlen($an_word)) == $an_word) return "an";
    }
    if(substr($word_lower,0,4) == "hour" and substr($word_lower,0,5) != "houri") return "an";

    // An 'an' letter (single letter word which should be preceeded by 'an')
    $an_letters = array('a','e','f','h','i','l','m','n','o','r','s','x');
    if(strlen($word) == 1) {
            if(in_array($word_lower,$an_letters)) return "an";
            else return "a";
    }

    // Capital words which should likely by preceeded by 'an'
    if(preg_match('/(?!FJO|[HLMNS]Y.|RY[EO]|SQU|(F[LR]?|[HL]|MN?|N|RH?|S[CHKLMNPTVW]?|X(YL)?)[AEIOU])[FHLMNRSX][A-Z]/', $word)) return "an";

    // Special cases where a word that begins with a vowel should be preceeded by 'a'
    $regex_array = array('^e[uw]','^onc?e\b','^uni([^nmd]|mo)','^u[bcfhjkqrst][aeiou]');
    foreach($regex_array as $regex) {
            if(preg_match('/'.$regex.'/',$word_lower)) return "a";        
    }

    // Special capital words
    if(preg_match('/^U[NK][AIEO]/',$word)) return "a";
    // Not sure what this does
    else if($word == strtoupper($word)) {
            $array = array('a','e','d','h','i','l','m','n','o','r','s','x');
            if(in_array($word_lower[0],$array)) return "an";
            else return "a";
    }

    // Basic method of words that begin with a vowel being preceeded by 'an'
    $vowels = array('a','e','i','o','u');
    if(in_array($word_lower[0],$vowels)) return "an";

    // Instances where y follwed by specific letters is preceeded by 'an'
    if(preg_match('/^y(b[lor]|cl[ea]|fere|gg|p[ios]|rou|tt)/', $word_lower)) return "an";

    // Default to 'a'
    return "a";
}

有一点(在评论下方“//不确定这是做什么”),我不确定它做了什么。如果有人能搞清楚,我会很高兴知道。

答案 6 :(得分:0)

基于规则的系统的问题在于它们处理边缘情况很差,并且它们很复杂。如果您可以根据实际数据做出决策,那么您会做得更好。在this answer中,我描述了如何使用维基百科构建查找字典,并使用这样的字典链接到(非常简单的)javascript实现。

前缀字典可以很好地处理首字母缩略词和数字,但是通过一些努力你可能会做得更好。

答案 7 :(得分:0)

我已经编写了一个流行的JS a-vs-an code的PHP端口,如此stackoverflow帖子https://stackoverflow.com/a/1288473/1526020中所述。

Github页面:https://github.com/UseAllFive/a-vs-an

E.g。

$result = $aVsAn->query('0800 number');
print_r($result);

返回

Array
(
    [aCount] => 8
    [anCount] => 25
    [prefix] => 08
    [article] => an
)
相关问题