【问题标题】:How can I remove all the vowels unless they are in word beginnings?除非它们在单词开头,否则如何删除所有元音?
【发布时间】:2021-11-12 01:33:18
【问题描述】:
$text = "I like apples more than oranges\n";

@words = split /” “/, $text;

foreach (@words) [1..] {
                      
    if $words "AEIOUaeiou";
                     
    $words =~ tr/A E I O U a e i o u//d;
}

print "$words\n";

“我喜欢苹果胜过橘子”会变成“我喜欢苹果先生”。 “I”中的“I”、“appls”中的“a”和“orngs”中的“o”将保留,因为它们是单词中的第一个字母。

这是我作为一年级学生的研究任务。我可以提出问题,然后再引用它们。请不要吝啬。

【问题讨论】:

  • 这段代码绝对不是 Perl 代码。您的第一步应该是学习如何编写 Perl 代码。谷歌搜索“Perl 教程”或“Perl 书”应该可以帮助您入门。然而,StackOverflow 并不是学习新语言语法的地方。
  • 我认为我错过了您抱怨我的评论的评论。我并不是要刻薄,只是想给你一些关于如何进行的建议,并澄清 StackOverflow 的用途和用途。特别是,StackOverflow 并不是“一个开发人员可以询问任何与编程相关的问题的网站”。我建议您阅读tour 以了解有关 StackOverflow 工作原理的更多信息。无论如何,要学习一门编程语言,您需要从基础开始:教程或书籍似乎是这方面的天然资源。

标签: perl


【解决方案1】:

我知道你说你不允许使用正则表达式,但是对于出现在这里的其他所有人,我将展示如何使用适当的工具。但是,然后我会用tr/// 做一些同样有用的事情。

编程(和数学)将看似困难的问题分解为更简单的问题的技巧之一,尤其是在您已经为简单问题找到解决方案的情况下。 (例如阅读Parnas decomposition)。

所以,问题是“我怎样才能删除所有的元音,除非它们在单词开头?” (在我把你的标题缩短一点之后)。这导致答案考虑单词,因此他们拆分输入,做了一些工作以确保他们没有处理第一个字符,然后重新组合结果。

但是,解决问题的另一种方法是“如何删除所有跟在另一个字母后面的元音?”。唯一不在另一个字母之后的字母是单词的第一个字母。

在另一个字母之后的元音的正则表达式很简单(但我会在这里坚持使用 ASCII,尽管它对于任何 Unicode 字母都一样简单):

[a-z][aeiou]

只有在第一个字母后有元音时才匹配。现在你想用任何东西替换所有这些。使用替换运算符s////g 标志进行所有全局替换,/i 使其不区分大小写:

s/[a-z][aeiou]//gi;

但是,有一个问题。它还替换了那个前导字母。这很容易修复。替换中的\K 表示忽略替换中模式之前的部分。 \K 之前的任何内容都不会被替换。所以,这只会替换元音:

s/[a-z]\K[aeiou]//gi;

但是,可能有元音相邻,所以在前面的“一个或多个”项中加上 + 量词:

s/[a-z]\K[aeiou]+//gi;

你根本不需要关心文字。

其他一些方法

说一个字母必须跟在另一个字母后面有一个特殊的零宽度断言:非单词边界,\B(尽管它也将数字和下划线算作“字母”):

s/\B[aeiou]+//gi;

\K 是在 v5.10 中引入的,它确实是一个具有可变宽度后视功能的绝妙技巧。但是,这里的后视是固定宽度:它是一个字符:

s/(?<=[a-z])[aeiou]+//gi;

但是,在意文字

假设您需要分别处理每个单词,以满足其他要求。看起来您已经混合了一些类似于 Python 的代码,如果 Perl 能够做到这一点,那就太好了 :)。问题并没有太大变化,因为您可以对每个单词做同样的事情。

foreach my $word ( split /\s+/, $x ) {
    .... # same thing for each word
    }

但是,这里有一个有趣的转折点?你如何将它们重新组合在一起?假设这是分隔符,其他解决方案只使用一个空格。也许应该有两个空格,或制表符,或其他什么。 split 具有特殊的“分隔符保留模式”,可以保留碎片之间的任何内容。当您在 split 模式中捕获时,这些捕获值是输出列表的一部分:

my @words_and_separators = split /(\s+)/, $x;

由于您知道分隔符都不会有元音,因此您可以对它们进行替换,因为它们不会改变。这意味着您可以像对待单词一样对待它们(也就是说,没有特殊情况,这是您分解问题时要考虑的另一件事)。要获得具有原始间距的最终字符串,请在空字符串上使用 join

my $ending_string = join '', @words_and_separators;

所以,这就是所有这些看起来如何组合在一起的方式。我将添加/r flag on the substitution so it returns the modified copy 而不是处理原始的(不要修改控制变量!):

my @words;
foreach my $word ( split /(\s+)/, $x ) {
    push @words, $word =~ s/\B[aeiou]+//gr;
    }
my $ending_string = join '', @words;

但是,foreach 有点烦人。这个列表管道是相同的,从下到上阅读这些更容易。每个事物都会产生一个列表,该列表流入它上面的事物。这就是我可能会用实际代码表达它的方式:

my $ending_string =
    join '',
    map { s/\B[aeiou]+//gr }   # each item is in $_
    split /(\s+)/, $x;

现在,这是大结局。如果我们不是在空格上而是在空格和每个单词的第一个字母上进行拆分呢?使用分隔符保留模式,我们知道我们只需要影响其他所有项目,所以我们像 map 一样计算它们:

my $n = 0;
my $ending_string =
    join '',
    map { ++$n % 2 ? tr/aeiouAEIOU//dr : $_ }
    split /((?:^|\s+)[a-z])/i, $x;

但是,我不会以这种方式编写这种技术,因为最终有人会找到我并进行报复。相反,我之前觉得很烦人的 foreach 可能会安抚愤怒的群众:

my $n = 0;
foreach ( split /((?:^|\s+)[a-z])/i, $x ) {
    print ++$n % 2 ? tr/aeiouAEIOU//dr : $_;
    }

现在这会记住原始字符串中的实际分隔符,并留下“单词”的第一个字符,因为它不在我们要修改的元素中。

foreach 中的代码不需要使用条件运算符?: 或其他一些功能。重要的部分是跳过所有其他元素。如果您以前没有见过split 模式,那它有点令人费解,但这就是您在满足这些要求时所得到的。我认为修改子字符串的一部分很可能会在第一次阅读时绊倒人们。

我的意思是,如果他们要让你在作业中以错误的方式做事,那就用一些会占用他们一些时间的东西来反击。 :)

哦,这很有趣

我有另一个想法,因为tr/// 除了音译还有另一个任务。它也很重要。因为它返回替换的数量,如果你用它自己替换任何东西,你会得到那个东西出现的计数。你可以数元音,例如:

my $has_vowels = $string =~ tr/aeiou/aeiou/;  # counts vowels

但是,对于一个字母的字符串,这意味着你有办法判断它是否是元音:

my $is_vowel = substr( $string, $i, 1 ) =~ tr/aeiou/aeiou/;

你也可以知道关于前一个字符的事情:

my $is_letter = substr( $string, $i - 1, 1 ) =~ tr/a-zA-Z/a-zA-Z/;

将它们放在一起,您可以查看任何位置并知道它是否是字母后面的元音。如果是这样,你跳过那封信。否则,您将该字母添加到输出中:

use v5.10;

$x = "I like apples       more than oranges  oooooranges\n";

my $output = substr $x, 0, 1; # avoid the -1 trap (end of string!)
for( my $i = 1; $i < length $x; $i++ ) {
    if( substr( $x, $i, 1 ) =~ tr/aeiou/aeiou/ ) { # is a vowel
        next if substr( $x, $i - 1, 1 ) =~ tr/a-zA-Z/a-zA-Z/;
        }

    $output .= substr $x, $i, 1;
    }

say $output;

这具有使用推荐的运算符但完全绕过意图的有趣结果。但是,这是 tr/// 的正确使用方式。

【讨论】:

  • 唯一不在另一个字母之后的字母是单词的第一个字母。认为就是这样 是的 :-)
  • 如果我们想包含其他字符,扩展定义非常容易。我的观点始终是调整定义以使事情变得简单。
【解决方案2】:

在接受这样的挑战之前,您似乎需要输入a little more effort into learning Perl。您的示例包含许多根本不是有效 Perl 的代码。

$x = "I like apples more than oranges\n";       #the original sentence
foreach $i in @x[1..] {

您将文本分配给标量变量$x,然后尝试使用数组变量@x。在 Perl 中,这是两个完全独立的变量,没有任何联系。此外,在 Perl 中,范围运算符 (..) 需要两端都有值。

如果您有一个名为 @x 的数组(而您没有,您有一个标量),那么您可以使用 foreach $i (@x) 来做您想做的事情

if $i "AEIOUaeiou";

我不确定你想在这里做什么。我想我能看到的最接近的有用 Perl 表达式是这样的:

if ($i =~ /^[AEIOUaeiou]$/)

这将测试$i 是否是元音。但这是一个正则表达式,所以你不能使用它。

显然,我会使用正则表达式来解决这个问题,但是由于那些被禁止,我已经在下面的代码中找到了一些稍微晦涩的 Perl 功能(这样你的老师不会相信这是你的解决方案,如果你只是剪切和粘贴它):

#!/usr/bin/perl

use strict;
use warnings;
use feature 'say';

my $text = "I like apples more than oranges\n";

# Split the string into an array of words
my @words = split /\s+/, $text;

# For each word...
for (@words) {
  # Get a substring that omits the first character
  # and use tr/// to remove vowels from that substring
  substr($_, 1) =~ tr/AEIOUaeiou//d;
}

# Join the array back together
$text = join ' ', @words;

say $text;

更新:哦,请注意我使用了tr/AEIUOaeiou//d,而您使用了tr/A E I O U a e i o u//d。它在这里可能不会有任何区别(取决于您的方法 - 但您可能会将其应用于不包含空格的字符串),但最好只包含您要删除的字符。

【讨论】:

  • 你好,这个答案是最有用的。谢谢。我有个问题。我可以用其他东西(例如引号)在 split 函数中更改此 \s+ 吗?有没有其他的写法?
  • @Traveller如果你读过the documentation for split(),你会看到它的第一个参数是一个正则表达式。因此,您可以将其设为任何正则表达式(有关详细信息,请参阅 the regex tutorial
【解决方案3】:

我们可以从末尾遍历输入字符串并删除任何前面没有空格的元音。我们从右到左,所以我们不必在每次删除后调整位置。我们不需要检查第一个字母,它不应该被删除。要删除元音,我们可以在原始字符串的substr 上使用tr///d

for my $i (reverse 1 .. length $x) {
    substr($x, $i, 1) =~ tr/aeiouAEIOU//d
        if substr($x, $i - 1, 1) ne ' ';
}

【讨论】:

    【解决方案4】:

    首先你的 if 语句是错误的。 其次,这不是 Perl 代码。 这是一段可以工作的代码,但是有更好的方法来做到这一点

    my $x = "I like apples more than oranges\n";
    my $new = "";   
    my @arr;
    foreach my $word (split(' ', $x)) {  
        @arr =  split('', $word);
        foreach (my $i; $i<scalar @arr; $i++){
            if ($i == 0){
                $new .= $arr[$i];
            }
            elsif (index("AEIOUaeiou", $arr[$i]) == -1) {
                $new .= $arr[$i];
            }   
        }
        $new .= " ";
    }
    print "$new\n";
    

    这里我拆分字符串以获得一个数组,然后我检查给定的字符是否是元音,如果不是,我将它附加到一个新字符串。

    始终包含

    use strict;
    use warnings;
    

    在您的代码之上。

    【讨论】:

    • 谢谢,但这段代码也删除了 a 和 o。
    • 1) 你不应该在循环之前声明@arr。 2) scalar @arr 可能只是 @arr。 3) foreach (my $i; $i&lt;scalar @arr; $i++) 会比for my $i (0 .. $#arr) 更清晰。 4) 对于包含字符的数组,@chars 将是一个比 @arr 更好的名称(由于@,很明显它无论如何都是一个数组。5)在split('',$word) 中省略括号更加惯用和轻量级: split //, $word.
    • 最后,OP明确表示他的问题是作业,完全是你为他做的。虽然帮助学生当然很好,但为他们做所有的工作会适得其反。特别是,OP 不知道 Perl 语法,因此,可能不知道这段代码是如何工作的,而且很可能只是盲目地复制粘贴它,而不学任何东西。 (嗯,这只是我的意见;您可以随意不同意并回答您喜欢的任何问题)
    • 他会复制并粘贴它,但这对他没有多大帮助。他需要自己理解,他需要坐下来学习语言。他是否有完整的答案,或者他没有,都无关紧要。另外我不知道你为什么在我的代码中指出这些改进,如果这是我的任务,我显然不会那样做。我不会给他完美的答案,如果他愿意,他可以自己阅读和改进。
    • 谢谢@IvanDimitrov 我理解你编写的代码,虽然部分考虑到我作为初学者的水平。我正在阅读您的代码并采用不同的方法。你的肯定很有帮助。
    【解决方案5】:

    显然这是一个左值练习。明显地。毫无疑问!

    #!/usr/bin/env perl
    # any old perl will do
    
    use 5.010;
    use strict;
    use warnings;
    
    # This is not idomatic nor fantastic code. Idiotastic?
    
    $_='I am yclept Azure-Orange, queueing to close a query. How are YOU?';
    
    # My little paws typed "local pos" and got
    # "Useless localization of match position" :(
    # so a busy $b keeps/restores that value
    
    while (/\b./g) {
        substr($_,$b=pos,/\b/g && -$b+pos)
          # Suggestion to use tr is poetic, not pragmatic,
          # ~ tr is sometimes y and y is sometimes a vowel
          =~ y/aeiouAEIOU//d;
        pos=$b;
    }
    
    say
    # "say" is the last word.
    

    是否禁止使用s/// 替换,或禁止使用所有正则表达式?出于某种原因,我认为匹配是可以的,而不是替代。如果匹配正常,我有一个想法,即通过模式匹配副作用删除$b 来“改进”这一点。看看能不能出。如果不是,应该很容易将/\b/pos 替换为index 和变量,尽管在这种情况下单词边界的定义过于简化了。

    (编辑)这里没有正则表达式更易读

    my $text="YO you are the one! The-only-person-  asking about double spaces.
    Unfortunate about newlines...";
    
    for (my $end=length $text;
         $end > 0 && (my $start = rindex $text,' ',$end);
         $end = $start-1) {
        # y is a beautiful letter, using it for vowels is poetry.
        substr($text,2+$start,$end-$start) =~ y/aeiouUOIEA//d;   
    }
    say $text;
    

    也许更狡猾的人会用 vec、unpack、open、fork 成功?

    您可以通过以下方式了解其中一些技术 perldoc -f substr perldoc -f pos perldoc re

    至于我自己的实现者笔记,最不重要的是没有标点符号结束,所以什么都不能跟上

    【讨论】:

      猜你喜欢
      • 2014-03-31
      • 2023-03-15
      • 2022-10-04
      • 1970-01-01
      • 2011-03-27
      • 2021-02-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多