【问题标题】:BASH Palindrome CheckerBASH 回文检查器
【发布时间】:2014-12-23 10:31:41
【问题描述】:

这是我第一次在这里发帖,请多多包涵。

我收到了一个 bash 作业,但我的教授完全没有帮助,他的笔记也是如此。

我们的任务是从文件中过滤和打印回文。在这种情况下,目录是:

/usr/share/dict/words

单词长度范围从 3 到 45,并且应该只过滤小写字母(给定的字典有字符和大写字母,以及小写字母)。即“-dkas-das”,所以像“q-evvavve-q”这样的东西可能算作回文,但我不应该得到正确的结果。

无论如何,我可以让它过滤掉 x 数量的单词并返回(虽然不是只过滤小写字母)。

grep "^...$" /usr/share/dict/words |
grep "\(.\).\1" 

我可以将后续行用于 5 个字母的单词和 7 个等等:

grep "^.....$" /usr/share/dict/words |
grep "\(.\)\(.\).\2\1" 

但是教授不希望这样。我们应该使用循环。我明白了这个概念,但我不知道语法,就像我说的那样,这些注释非常无用。

我尝试设置变量 x=... 和 y=.. 并在 while 循环中设置 x=$x$y 但这不起作用(语法错误),x+=.. 也没有/p>

感谢任何帮助。甚至过滤掉我的非小写字母。

谢谢!

编辑:

如果您要提供解决方案或解决方案的提示,则首选最简单的方法。 最好使用 2 个 grep 语句和一个循环。

再次感谢。

【问题讨论】:

    标签: bash loops grep palindrome


    【解决方案1】:

    像这样:

    for word in `grep -E '^[a-z]{3,45}$' /usr/share/dict/words`;
        do [ $word == `echo $word | rev` ] && echo $word;
    done;
    

    使用我的字典输出:

    aha
    bib
    bob
    boob
    ...
    wow
    

    更新

    正如 cmets 中所指出的,将字典中的大部分内容读入 for 循环中的变量可能不是最有效的,并且可能会在某些 shell 中触发错误。这是一个更新的版本:

    grep -E '^[a-z]{3,45}$' /usr/share/dict/words | while read -r word;
        do [ $word == `echo $word | rev` ] && echo $word;
    done;
    

    【讨论】:

    • 好吧,它确实有效,但对我来说,它列出了单词,我必须为每个单词按 ENTER。如果符合条件,它将显示,如果不符合条件,则保持空白。另外,我必须使用 grep,但仍然感谢。
    • 每次回声后都必须按回车键很奇怪。不就是慢吗?我在我的解决方案中使用grepegrepgrep -E 相同。
    • 嗯,它仍然在做我必须按 Enter 的事情。如果我记得,我第一次复制它时,它工作但崩溃了。如果这有什么不同,我正在使用 PuTTY。
    • 在某些 shell 中,这将产生 argument list too long,因为您实际上是在将整个字典作为 for 循环的参数读取。使用grep ... /usr/share/dict/words | while read -r word; do 会更经济……不过,对每个单词运行外部进程仍需要很长时间。
    • 明白了。考虑到我们在课堂上学到的知识以及教授对我们的期望,我真的不知道这需要多长时间。它分类了超过 479,000 个单词。如果我明白你在说什么,我认为 Tripleee 所说的一次过关是关键。我不知道他为什么会想要一个循环,但我想一个循环可以实现,也可以只做一次?我可能只是需要更多的思考。
    【解决方案2】:

    为什么使用grep? Bash 很乐意为您做到这一点:

    #!/bin/bash
    
    is_pal() {
        local w=$1
        while (( ${#w} > 1 )); do
            [[ ${w:0:1} = ${w: -1} ]] || return 1
            w=${w:1:-1}
        done
     }
    
     while read word; do
         is_pal "$word" && echo "$word"
     done
    

    将其保存为bananachmod +x banana 并享受:

    ./banana < /usr/share/dict/words
    

    如果您只想保留至少三个字符的单词:

    grep ... /usr/share/dict/words | ./banana
    

    如果您只想保留仅包含小写且至少包含三个字母的单词:

    grep '^[[:lower:]]\{3,\}$' /usr/share/dict/words | ./banana
    

    【讨论】:

      【解决方案3】:

      多个greps 是浪费。你可以简单地做

      grep -E '^([a-z])[a-z]\1$' /usr/share/dict/words
      

      一气之下,类似地,将表达式放在grep 的标准输入上,如下所示:

      echo '^([a-z])[a-z]\1$
      ^([a-z])([a-z])\2\1$
      ^([a-z])([a-z])[a-z]\2\1$' | grep -E -f - /usr/share/dict/words
      

      但是,常规 grep 不允许反向引用超出 \9。使用grep -P,您也可以使用两位数的反向引用。

      以下脚本在循环中构造整个表达式。不幸的是,grep -P 不允许使用 -f 选项,因此我们构建了一个大的 thumpin' 变量来保存该模式。然后我们实际上也可以简化为^(.)(?:.|(.)(?:.|(.)....\3)?\2?\1$ 形式的单一模式,除了我们使用[a-z] 而不是. 来限制为小写。

      head=''
      tail=''
      for i in $(seq 1 22); do
          head="$head([a-z])(?:[a-z]|"
          tail="\\$i${tail:+)?}$tail"
      done
      grep -P "^${head%|})?$tail$" /usr/share/dict/words
      

      单个grep 应该比在大型输入文件上单独调用grep 22 或43 次要快得多。如果要按长度排序,只需将其添加为管道末尾的过滤器即可;它仍然应该比整个字典的多次传递要快。

      仅当tail 为非空时,表达式${tail+:)?} 才计算为右括号和问号,这是强制\1 后向引用为非可选的便捷方法。有点类似,${head%|}$head 的最终值中修剪出最终的交替运算符。

      【讨论】:

      • 好的,我认为这绝对有帮助。无论如何,教授不想要超过 2 个 grep 语句。同样,我对 bash 几乎是全新的,并且只有有限的编程经验。我想如果我把它分解并改变一些东西,它可能会起作用。谢谢。
      • 更新为只查看小写单词——使用[a-z]而不是.。如果您想要不区分大小写的匹配,请将-i 选项添加到grep。如果要包含文字连字符,可以将范围扩展到 [-a-z]
      • 进一步更新为使用grep -P,以适应\9之外的反向引用。
      • 您还可以在字符类[-'a-z] 中添加撇号——我的/usr/share/dict/words 中有ma'am
      【解决方案4】:

      好的,这里有一些东西可以帮助您入门:

      我建议使用您上面的计划,只需生成“。”的数量。使用 for 循环。

      这个问题将解释如何从3到45进行for循环:

      How do I iterate over a range of numbers defined by variables in Bash?

      for i in {3..45}; 
      do 
         * put your code above here *
      done
      

      现在你只需要弄清楚如何使“i”个点数“。”在你的第一个 grep 中,你就完成了。

      另外,看看 sed,它可以为你核对非小写的答案..

      【讨论】:

      • 好的,谢谢你的开始。我觉得这可能会把我带到某个地方。
      【解决方案5】:

      另一个使用与 Perl 兼容的正则表达式 (PCRE) 和递归的解决方案,大量this answer的启发:

      grep -P '^(?:([a-z])(?=[a-z]*(\1(?(2)\2))$))++[a-z]?\2?$' /usr/share/dict/words
      

      【讨论】:

        猜你喜欢
        • 2022-11-10
        • 1970-01-01
        • 2012-03-30
        • 1970-01-01
        • 1970-01-01
        • 2015-04-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多