在给定的文本中建立一个ASCII图表,显示最常用的单词。

156

挑战:

构建一个给定文本中最常用单词的ASCII图表。

规则:

  • 只接受a-zA-Z(字母字符)作为单词的一部分。
  • 忽略大小写(She == she,我们的目的是相同的)。
  • 忽略以下单词(相当武断,我知道):the, and, of, to, a, i, it, in, or, is
  • 澄清:考虑到don't:在a-zA-Z范围内,这将被视为2个不同的“单词”:(dont)。

  • 可选地(现在正式更改规格已经太晚了),您可以选择删除所有单个字母的“单词”(这可能会导致忽略列表缩短)。

解析给定的文本(通过命令行参数或管道读取指定的文件;假设为us-ascii),并构建一个以下特征的词频图表

  • 显示最常见的22个单词的图表(按降序排列)。
  • 条形宽度表示单词出现的次数(频率比例)。在单词后面添加一个空格并打印单词。
  • 确保这些条形图(加上空格-单词-空格)总是适合bar + [space] + word + [space]应始终<= 80个字符(确保您考虑可能不同的条形和单词长度:例如:第二个最常见的单词可能比第一个长得多,而在频率上并没有那么大的差异)。在这些约束条件下最大化条形宽度,并根据它们所代表的频率适当缩放条形。

示例:

示例文本 在这里找到《爱丽丝梦游仙境》,刘易斯·卡罗尔著)。

此特定文本将产生以下图表:

 _________________________________________________________________________
|_________________________________________________________________________| 她 
|_______________________________________________________________| 你 
|____________________________________________________________| 说 
|____________________________________________________| 爱丽丝 
|______________________________________________| 是 
|__________________________________________| 那个 
|___________________________________| 如同 
|_______________________________| 她的 
|____________________________| 和 
|____________________________| 在 
|___________________________| 的 
|___________________________| 特 
|_________________________| 定 
|_________________________| 所有 
|______________________| 这个 
|______________________| 对于 
|______________________| 有 
|_____________________| 但是 
|____________________| 是 
|____________________| 不 
|___________________| 他们 
|__________________| 这么

供您参考:上述图表所建立的频率如下:

与编程相关的内容:
将链接的《爱丽丝梦游仙境》文件中每个“you”替换为“superlongstringstring”:
[('她', 553), ('你', 481), ('说', 462), ('爱丽丝', 403), ('是', 358), ('那', 330), ('作为', 274), ('她的', 248), ('与', 227), ('在', 227), ('s', 219), ('t', 218), ('上', 204), ('所有', 200), ('这', 181), ('为', 179), ('有', 178), ('但', 175), ('是', 167), ('不', 166), ('他们', 155), ('如此', 152)]
获胜者:

最短的解决方案(按每种语言的字符计数)。祝玩得愉快!


编辑:总结到目前为止的结果表(2012-02-15)(最初由用户Nas Banov添加):

语言               宽松     严格
=========         =======  ======
GolfScript          130     143
Perl                        185
Windows PowerShell  148     199
Mathematica                 199
Ruby                185     205
Unix Toolchain      194     228
Python              183     243
Clojure                     282
Scala                       311
Haskell                     333
Awk                         336
R                   298
Javascript          304     354
Groovy              321
Matlab                      404
C#                          422
Smalltalk           386
PHP                 450
F#                          452
TSQL                483     507

这些数字代表特定语言中最短解决方案的长度。“严格”是指实现了规范的解决方案(绘制|____|条,用一条____线在顶部关闭第一个条,考虑具有高频率的长单词等)。"宽松"意味着采取了某些自由以缩短解决方案。

仅包含长度小于500个字符的解决方案。语言列表按“严格”解决方案的长度排序。“Unix工具链”用于表示使用传统*nix shell 加上一些工具(如grep、tr、sort、uniq、head、perl、awk)的各种解决方案。


4
“最长的酒吧”加上一个单词可能达不到80个字符,如果第二常见的单词比较长的话。我想我在寻找“最大约束”。 - Brian
2
在我看来,让这个程序在执行时间和内存使用方面都表现出色,似乎比字符计数更具有挑战性。 - Frank Farmer
81
很高兴看到我最喜欢的词 st 得到了体现。 - indiv
1
@Frank,是的,但那样就不算代码高尔夫了。 - JSBձոգչ
8
@indiv,@Nas Banov -- 简单得有些傻的分词器会把"didn't" 分成 {didn, t},把 "she's" 分成 {she, s} :) - hobbs
显示剩余29条评论
59个回答

19

Ruby,215,216218221224236237个字符

更新1:太好了!与JS Bangs解决方案并列。想不出更多缩短的方法:)

更新2:玩了个小花招。将each改为map以节省1个字符:)

更新3:将File.read改为IO.read+2。Array.group_by没有什么收获,改用reduce+6。在正则表达式中使用downcase转换为小写后,不需要进行大小写不敏感的检查+1。通过取反来降序排序很容易实现+6。总共节省15个字符

更新4:使用[0]而不是.first+3。(@Shtééf)

更新5:在原地扩展变量l+1。在原地扩展变量s+2。(@Shtééf)

更新6:对于第一行,使用字符串拼接而不是插值+2。(@Shtééf)

w=(IO.read($_).downcase.scan(/[a-z]+/)-%w{the and of to a i it in or is}).reduce(Hash.new 0){|m,o|m[o]+=1;m}.sort_by{|k,v|-v}.take 22;m=76-w[0][0].size;puts' '+'_'*m;w.map{|x,f|puts"|#{'_'*(f*1.0/w[0][1]*m)}| #{x} "}

更新7:我通过使用实例变量来检测循环中的第一次迭代,经历了很多麻烦。但是我只得到了+1,尽管可能有潜力。保留先前的版本,因为我认为这个版本是黑魔法。(@Shtééf)

(IO.read($_).downcase.scan(/[a-z]+/)-%w{the and of to a i it in or is}).reduce(Hash.new 0){|m,o|m[o]+=1;m}.sort_by{|k,v|-v}.take(22).map{|x,f|@f||(@f=f;puts' '+'_'*(@m=76-x.size));puts"|#{'_'*(f*1.0/@f*@m)}| #{x} "}

易读版本

string = File.read($_).downcase

words = string.scan(/[a-z]+/i)
allowed_words = words - %w{the and of to a i it in or is}
sorted_words = allowed_words.group_by{ |x| x }.map{ |x,y| [x, y.size] }.sort{ |a,b| b[1] <=> a[1] }.take(22)
highest_frequency = sorted_words.first
highest_frequency_count = highest_frequency[1]
highest_frequency_word = highest_frequency[0]

word_length = highest_frequency_word.size
widest = 76 - word_length

puts " #{'_' * widest}"    
sorted_words.each do |word, freq|
  width = (freq * 1.0 / highest_frequency_count) * widest
  puts "|#{'_' * width}| #{word} "
end

使用方法:

echo "Alice.txt" | ruby -ln GolfedWordFrequencies.rb

输出:

 _________________________________________________________________________
|_________________________________________________________________________| she 
|_______________________________________________________________| you 
|____________________________________________________________| said 
|_____________________________________________________| alice 
|_______________________________________________| was 
|___________________________________________| that 
|____________________________________| as 
|________________________________| her 
|_____________________________| with 
|_____________________________| at 
|____________________________| s 
|____________________________| t 
|__________________________| on 
|__________________________| all 
|_______________________| this 
|_______________________| for 
|_______________________| had 
|_______________________| but 
|______________________| be 
|_____________________| not 
|____________________| they 
|____________________| so 

3
“p” 不是“puts”的快捷方式吗?这可以缩短一些代码。 - rfusca
1
不错。你使用 scan 的方式给了我更好的想法,所以我又领先了一步 :)。 - JSBձոգչ
2
你需要对条形图进行缩放,以便最长的单词及其条形可以适合80个字符。正如Brian建议的那样,过长的第二个单词会破坏你的程序。 - Gabe
3
我不知道为什么这个问题还在收集投票。该解决方案(在一般情况下)是不正确的,现在已经有两种更短的 Ruby 解决方案了。 - Joey
1
如果我错了,请纠正我,但是为什么不使用REGEXP的不区分大小写标志,而不是使用“downcase”,这可以节省6-7个字节,不是吗? - st0le
显示剩余8条评论

19

Python 2.x, 大包容的方法 = 227 183 字符

import sys,re
t=re.split('\W+',sys.stdin.read().lower())
r=sorted((-t.count(w),w)for w in set(t)if w not in'andithetoforinis')[:22]
for l,w in r:print(78-len(r[0][1]))*l/r[0][0]*'=',w

允许在实现中自由选择,我构建了一个字符串连接,其中包含所有请求排除的单词(the, and, of, to, a, i, it, in, or, is)- 此外还排除了示例中的两个臭名昭著的“单词”st - 我还免费添加了an, for, he的排除。我尝试将这些单词的所有组合与Alice、《圣经》和Jargon文件中的单词语料库进行比较,以查看是否有任何单词会被字符串错误地排除。这就是我得出两个排除字符串的方法:itheandtoforinisandithetoforinis
PS. 借鉴其他解决方案来缩短代码。
=========================================================================== she 
================================================================= you
============================================================== said
====================================================== alice
================================================ was
============================================ that
===================================== as
================================= her
============================== at
============================== with
=========================== on
=========================== all
======================== this
======================== had
======================= but
====================== be
====================== not
===================== they
==================== so
=================== very
=================== what
================= little

抱怨

关于需要忽略的单词,人们可能会认为这些单词来自英语中最常用的单词列表。该列表取决于所使用的文本语料库。根据最受欢迎的列表之一(http://en.wikipedia.org/wiki/Most_common_words_in_Englishhttp://www.english-for-students.com/Frequently-Used-Words.htmlhttp://www.sporcle.com/games/common_english_words.php),前十个单词是:the be(am/are/is/was/were) to of and a in that have I

《爱丽丝梦游仙境》文本中的前十个单词是:the and to a of it she i you said
《黑客文化词典》(v4.4.7)中的前十个单词是:the a of to and in is that or for

因此问题是为什么在问题的忽略列表中包括了单词or,它在流行程度上排名第30,而单词that(排名第8)没有被包括。因此我认为应该动态提供忽略列表(或可以省略)。

另一个想法是简单地跳过结果中的前十个单词-这实际上会缩短解决方案(需要显示的仅为第11到32个条目)。


Python 2.x,严谨的方法= 277 243个字符

上述代码中绘制的图表是简化的(仅使用一个字符表示条形)。如果要精确复制问题描述中的图表(不是必需的),则可以使用此代码:

import sys,re
t=re.split('\W+',sys.stdin.read().lower())
r=sorted((-t.count(w),w)for w in set(t)-set(sys.argv))[:22]
h=min(9*l/(77-len(w))for l,w in r)
print'',9*r[0][0]/h*'_'
for l,w in r:print'|'+9*l/h*'_'+'|',w

我对选择要排除的10个词语 - the, and, of, to, a, i, it, in, or, is 感到有些随意。因此,这些词语可以作为命令行参数传递,格式如下:
python WordFrequencyChart.py the and of to a i it in or is <"Alice's Adventures in Wonderland.txt"

这段文字包含213个字符,如果计算上通过命令行传递的原始忽略列表,则字符数为243。

另外,第二行代码还会根据所有最高频单词的长度进行调整,以避免在特殊情况下出现文本溢出。

 _______________________________________________________________
|_______________________________________________________________| she
|_______________________________________________________| superlongstringstring
|_____________________________________________________| said
|______________________________________________| alice
|_________________________________________| was
|______________________________________| that
|_______________________________| as
|____________________________| her
|__________________________| at
|__________________________| with
|_________________________| s
|_________________________| t
|_______________________| on
|_______________________| all
|____________________| this
|____________________| for
|____________________| had
|____________________| but
|___________________| be
|___________________| not
|_________________| they
|_________________| so

到目前为止,这是一个不错的解决方案,尽管“忽略列表”尚未实现,而且进度条目前有些简陋。 - ChristopheD
@ChristopheD:它是存在的,但没有“用户指南”。刚刚添加了一大段文本。 - Nas Banov
关于您的语言和解决方案列表:请寻找使用\W分割或在正则表达式中使用\b的解决方案,因为它们很可能不符合规范,这意味着它们不会在数字或_上进行分割,也可能不会从字符串中删除停用词,例如the_foo_or123bar。它们可能不会出现在测试文本中,但规范在这种情况下非常清楚。 - Joey
太棒了,Nas!我花了一个下午来优化这个程序,只找到了一处改进。你可以通过删除 sys.argv 的 hack 并使用以下代码将其缩减为 239 个字符:re.findall(r'\b(?!(?:the|and|.|of|to|i[tns]|or)\b)\w+',sys.stdin.read().lower()) - intgr

12

Haskell - 333个字符

(在main中增加了一个换行以提高可读性,在最后一行不需要换行。)

import Data.List
import Data.Char
l=length
t=filter
m=map
f c|isAlpha c=toLower c|0<1=' '
h w=(-l w,head w)
x!(q,w)='|':replicate(minimum$m(q?)x)'_'++"| "++w
q?(g,w)=q*(77-l w)`div`g
b x=m(x!)x
a(l:r)=(' ':t(=='_')l):l:r
main=interact$unlines.a.b.take 22.sort.m h.group.sort
  .t(`notElem`words"the and of to a i it in or is").words.m f

最好通过阅读对“交互”参数的解释来理解它的工作原理:
- map f将字母小写,用空格替换其他字符。 - words生成一个单词列表,去除分隔空白。 - filter (notElem words "the and of to a i it in or is")丢弃所有包含禁止单词的条目。 - group . sort将单词排序,并将相同的单词分组到列表中。 - map h将每个相同单词的列表映射到形式为(-frequency, word)的元组。 - take 22 . sort按降序(第一个元素)对元组进行排序,并仅保留前22个元组。 - b将元组映射到条形图(见下文)。 - a在顶部加上第一行下划线,以完成最上面的条形图。 - unlines使用换行符将所有这些行连接在一起。
难点是正确地获取条形图长度。我假设只有下划线计入条形图的长度,因此||将是零长度的条形图。函数bc x映射到x的直方图列表中,其中x是整个列表传递给c,以便每次调用c可以通过调用u来计算自己的比例因子。通过这种方式,我避免使用浮点数或有理数,它们的转换函数和导入将消耗许多字符。
注意使用-frequency的技巧。这消除了需要reverse sort的需要,因为按(升序)-frequency排序会将具有最大频率的单词放在首位。稍后,在函数u中,两个-frequency值相乘,这将取消负号。

非常好的工作(想点赞,但今天已经用完了所有在这个帖子中的优秀回答的投票)。 - ChristopheD
实际上,这仍然是相当惯用的 Haskell 代码,尽管不是非常高效。短名称使它看起来比实际情况更糟糕。 - Thomas
u q(g,w)=q*div(77-l w)g -- 可以为您节省2个字符 - Edward Kmett
1
无法移动 div!实际上尝试一下-输出是错误的。原因是在 * 之前执行 div 会失去精度。 - MtnViewMark
@trinithis:确实更短了,但现在我不再理解它是如何工作的了!恐怕你已经超出了我对Haskell的理解。为什么需要一个惊叹号模式?问号甚至代表什么意思? - Thomas
显示剩余5条评论

11

JavaScript 1.8 (SpiderMonkey) - 354

x={};p='|';e=' ';z=[];c=77
while(l=readline())l.toLowerCase().replace(/\b(?!(the|and|of|to|a|i[tns]?|or)\b)\w+/g,function(y)x[y]?x[y].c++:z.push(x[y]={w:y,c:1}))
z=z.sort(function(a,b)b.c-a.c).slice(0,22)
for each(v in z){v.r=v.c/z[0].c
c=c>(l=(77-v.w.length)/v.r)?l:c}for(k in z){v=z[k]
s=Array(v.r*c|0).join('_')
if(!+k)print(e+s+e)
print(p+s+p+e+v.w)}

不幸的是,Rhino版本中的for([k,v]in z)在SpiderMonkey中似乎无法正常工作,而readFile()比使用readline()要容易一些,但升级到1.8版本后,我们可以使用函数闭包来减少更多的代码行....

为了可读性添加空格:

x={};p='|';e=' ';z=[];c=77
while(l=readline())
  l.toLowerCase().replace(/\b(?!(the|and|of|to|a|i[tns]?|or)\b)\w+/g,
   function(y) x[y] ? x[y].c++ : z.push( x[y] = {w: y, c: 1} )
  )
z=z.sort(function(a,b) b.c - a.c).slice(0,22)
for each(v in z){
  v.r=v.c/z[0].c
  c=c>(l=(77-v.w.length)/v.r)?l:c
}
for(k in z){
  v=z[k]
  s=Array(v.r*c|0).join('_')
  if(!+k)print(e+s+e)
  print(p+s+p+e+v.w)
}

用法: js golf.js < input.txt

输出:

 _________________________________________________________________________ 
|_________________________________________________________________________| 她
|_______________________________________________________________| 你
|____________________________________________________________| 说
|____________________________________________________| 爱丽丝
|______________________________________________| 是
|___________________________________________| 那个
|___________________________________| 当作
|________________________________| 她的
|_____________________________| 在
|_____________________________| 和
|____________________________| 的
|____________________________| t
|__________________________| 上
|_________________________| 所有的
|_______________________| 这个
|______________________| 对于
|______________________| 有
|______________________| 但是
|_____________________| 是
|_____________________| 不是
|___________________| 他们
|___________________| 如此

(基础版-无法正确处理条形宽度)

JavaScript(Rhino) -405 395 387 377 368 343 304个字符

我认为我的排序逻辑有误,但..我不知道。脑抽修复。

缩小(滥用\n有时被解释为;):

x={};p='|';e=' ';z=[]
readFile(arguments[0]).toLowerCase().replace(/\b(?!(the|and|of|to|a|i[tns]?|or)\b)\w+/g,function(y){x[y]?x[y].c++:z.push(x[y]={w:y,c:1})})
z=z.sort(function(a,b){return b.c-a.c}).slice(0,22)
for([k,v]in z){s=Array((v.c/z[0].c)*70|0).join('_')
if(!+k)print(e+s+e)
print(p+s+p+e+v.w)}

啊,先生。我相信这是您的手套。请让您的第二个人与我的交谈。 - dmckee --- ex-moderator kitten
2
顺便说一下--我很喜欢i[tns]?这一部分。非常狡猾。 - dmckee --- ex-moderator kitten
@dmckee - 干得好,我想我打不过你的336分,享受你应得的点赞吧 :) - Matt
你绝对可以打败336...有一个23个字符的切割可用--.replace(/[^\w ]/g, e).split(/\s+/).map(可以替换为.replace(/\w+/g,并使用与你的.map相同的函数...不确定Rhino是否支持function(a,b)b.c-a.c而不是你的排序函数(spidermonkey支持),但这将节省{return}... b.c-a.c是比a.c<b.c更好的排序...在底部编辑一个Spidermonkey版本,进行这些更改。 - gnarf
我将我的SpiderMonkey版本提升到顶部,因为它符合条形宽度约束... 通过使用负向先行断言正则表达式来拒绝单词,从而允许进行单个replace(),并使用?:缩短了一些if语句。虽然如此,这是一个很好的基础! - gnarf
这不会消除数字或下划线包围的停用词,例如在 foo_the123 中,只应保留 foo - Joey

11

Perl, 189个字符/205个字符(完全实现)

部分灵感来自早期的perl/ruby提交,一些类似的想法是独立到达的,其他则是原创的。更短的版本还包括我从其他提交中看到/学到的一些东西。

$k{$_}++for grep{$_!~/^(the|and|of|to|a|i|it|in|or|is)$/}map{lc=~/[a-z]+/g}<>;@t=sort{$k{$b}<=>$k{$a}}keys%k;$l=76-length$t[0];printf" %s
",'_'x$l;printf"|%s| $_
",'_'x int$k{$_}/$k{$t[0]}*$l for@t[0..21];

最新版本降至 191个字符:

/^(the|and|of|to|.|i[tns]|or)$/||$k{$_}++for map{lc=~/[a-z]+/g}<>;@e=sort{$k{$b}<=>$k{$a}}keys%k;$n=" %s
";$r=(76-y///c)/$k{$_=$e[0]};map{printf$n,'_'x($k{$_}*$r),$_;$n="|%s| %s
"}@e[0,0..21]

最新版本仅有189个字符:

/^(the|and|of|to|.|i[tns]|or)$/||$k{$_}++for map{lc=~/[a-z]+/g}<>;@_=sort{$k{$b}<=>$k{$a}}keys%k;$n=" %s
";$r=(76-m//)/$k{$_=$_[0]};map{printf$n,'_'x($k{$_}*$r),$_;$n="|%s| %s
"}@_[0,0..21]

这个版本(205个字符)考虑了后面可能出现的单词长度超过当前行的情况。
/^(the|and|of|to|.|i[tns]|or)$/||$k{$_}++for map{lc=~/[a-z]+/g}<>;($r)=sort{$a<=>$b}map{(76-y///c)/$k{$_}}@e=sort{$k{$b}<=>$k{$a}}keys%k;$n=" %s
";map{printf$n,'_'x($k{$_}*$r),$_;$n="|%s| %s
";}@e[0,0..21]

11

Python 3.1 - 245 229 个字符

我猜使用Counter可能有点作弊了 :) 一周前我刚刚读到它,所以这是完美的机会来看看它是如何工作的。

import re,collections
o=collections.Counter([w for w in re.findall("[a-z]+",open("!").read().lower())if w not in"a and i in is it of or the to".split()]).most_common(22)
print('\n'.join('|'+76*v//o[0][1]*'_'+'| '+k for k,v in o))

打印输出:

|____________________________________________________________________________| she
|__________________________________________________________________| you
|_______________________________________________________________| said
|_______________________________________________________| alice
|_________________________________________________| was
|_____________________________________________| that
|_____________________________________| as
|__________________________________| her
|_______________________________| with
|_______________________________| at
|______________________________| s
|_____________________________| t
|____________________________| on
|___________________________| all
|________________________| this
|________________________| for
|________________________| had
|________________________| but
|______________________| be
|______________________| not
|_____________________| they
|____________________| so

其中部分代码是从AKX的解决方案中“借用”的。


第一行缺失。并且进度条长度不正确。 - Joey
在你的代码中似乎open('!')从标准输入读取 - 这是哪个版本/操作系统?还是必须将文件命名为'!'? - Nas Banov
给文件命名为"!" :) 抱歉之前表述不够清晰,我应该提前说明的。 - Sam Dolan

11

PHP CLI 版本(450 字符)

这个解决方案考虑了大多数纯粹主义者方便地选择忽略的最后一个要求。这花费了 170 个字符!

用法:php.exe <this.php> <file.txt>

压缩版:

<?php $a=array_count_values(array_filter(preg_split('/[^a-z]/',strtolower(file_get_contents($argv[1])),-1,1),function($x){return !preg_match("/^(.|the|and|of|to|it|in|or|is)$/",$x);}));arsort($a);$a=array_slice($a,0,22);function R($a,$F,$B){$r=array();foreach($a as$x=>$f){$l=strlen($x);$r[$x]=$b=$f*$B/$F;if($l+$b>76)return R($a,$f,76-$l);}return$r;}$c=R($a,max($a),76-strlen(key($a)));foreach($a as$x=>$f)echo '|',str_repeat('-',$c[$x]),"| $x\n";?>

易读的人类语言:

<?php

// Read:
$s = strtolower(file_get_contents($argv[1]));

// Split:
$a = preg_split('/[^a-z]/', $s, -1, PREG_SPLIT_NO_EMPTY);

// Remove unwanted words:
$a = array_filter($a, function($x){
       return !preg_match("/^(.|the|and|of|to|it|in|or|is)$/",$x);
     });

// Count:
$a = array_count_values($a);

// Sort:
arsort($a);

// Pick top 22:
$a=array_slice($a,0,22);


// Recursive function to adjust bar widths
// according to the last requirement:
function R($a,$F,$B){
    $r = array();
    foreach($a as $x=>$f){
        $l = strlen($x);
        $r[$x] = $b = $f * $B / $F;
        if ( $l + $b > 76 )
            return R($a,$f,76-$l);
    }
    return $r;
}

// Apply the function:
$c = R($a,max($a),76-strlen(key($a)));


// Output:
foreach ($a as $x => $f)
    echo '|',str_repeat('-',$c[$x]),"| $x\n";

?>

输出:

|-------------------------------------------------------------------------| she
|---------------------------------------------------------------| you
|------------------------------------------------------------| said
|-----------------------------------------------------| alice
|-----------------------------------------------| was
|-------------------------------------------| that
|------------------------------------| as
|--------------------------------| her
|-----------------------------| at
|-----------------------------| with
|--------------------------| on
|--------------------------| all
|-----------------------| this
|-----------------------| for
|-----------------------| had
|-----------------------| but
|----------------------| be
|---------------------| not
|--------------------| they
|--------------------| so
|-------------------| very
|------------------| what

当单词很长时,横杠会被适当调整:

|--------------------------------------------------------| she
|---------------------------------------------------| thisisareallylongwordhere
|-------------------------------------------------| you
|-----------------------------------------------| said
|-----------------------------------------| alice
|------------------------------------| was
|---------------------------------| that
|---------------------------| as
|-------------------------| her
|-----------------------| with
|-----------------------| at
|--------------------| on
|--------------------| all
|------------------| this
|------------------| for
|------------------| had
|-----------------| but
|-----------------| be
|----------------| not
|---------------| they
|---------------| so
|--------------| very

10

Perl: 203 202 201 198 195 208 203 / 231 字符

$/=\0;/^(the|and|of|to|.|i[tns]|or)$/i||$x{lc$_}++for<>=~/[a-z]+/gi;map{$z=$x{$_};$y||{$y=(76-y///c)/$z}&&warn" "."_"x($z*$y)."\n";printf"|%.78s\n","_"x($z*$y)."| $_"}(sort{$x{$b}<=>$x{$a}}keys%x)[0..21]

另一种完整的实现,包括指定行为(全局压缩)用于病态情况,即第二个单词既流行又足够长,组合超过80个字符(此实现为231个字符):

$/=\0;/^(the|and|of|to|.|i[tns]|or)$/i||$x{lc$_}++for<>=~/[a-z]+/gi;@e=(sort{$x{$b}<=>$x{$a}}keys%x)[0..21];for(@e){$p=(76-y///c)/$x{$_};($y&&$p>$y)||($y=$p)}warn" "."_"x($x{$e[0]}*$y)."\n";for(@e){warn"|"."_"x($x{$_}*$y)."| $_\n"}
规范没有说明必须使用STDOUT,所以我使用了Perl的warn()而不是print - 这样就省了四个字符。用map代替foreach,但我觉得在split(join())中仍然可以节省一些字符。无论如何,我已将其减少到203个字符 - 可能需要睡一觉再来优化。至少现在Perl的字符数比“shell,grep,tr,grep,sort,uniq,sort,head,perl”要少;)
PS:Reddit问候大家;)
更新:取消了join()并采用赋值和隐式标量转换join。现在小于202个字符。请注意,我利用了可选的“忽略1字母单词”的规则,将2个字符削减掉,因此请记住频率计数将反映此内容。
更新2:使用$/删除赋值和隐式连接,以便首先使用<>一次性获取文件。虽然大小相同,但更加丑陋。将if(!$y){}替换为$y||{}&&,又节省了1个字符=>201。
更新3:通过将lc移到map块外部来控制小写(lc<>)- 交换两个正则表达式以不再使用/i选项,因为它不再需要。用传统的perlgolf ||隐式条件结构替换了显式条件x? y: z结构- /^...$/i?1:$x{$}++替换为/^...$/||$x{$}++,节省了三个字符!=>198,突破了200的壁垒。也许很快就要睡觉了...
更新4:睡眠不足让我变得疯狂。好吧。更疯狂。想到这只需要解析正常的文本文件,如果遇到空值就放弃它。又省了两个字符。用比“length”更短1个字符(且更适合高尔夫运动的)y///c代替了“length”,你听到我了吗,GolfScript?我来了!!哭泣声 更新5:睡眠不足让我忘记22行限制和随后行限制。 处理这些后回到208。花13个字符处理它并不是世界末日。尝试使用Perl的正则表达式内联评估,但很难让它既工作保存字符...lol。更新示例以匹配当前输出。
更新6:删除保护(...)for的不必要括号,因为语法糖++允许对其进行压缩。感谢Chas. Owens的意见(提醒我疲惫的大脑),将i[tns]字符类解决方案加入其中。又降至203。
更新7:添加了第二个任务,完整实现规范(包括对于次要长单词的完全压缩行为,而不是大多数人根据原始规范而进行截断)。
 _________________________________________________________________________
|_________________________________________________________________________| she
|_______________________________________________________________| you
|____________________________________________________________| said
|_____________________________________________________| alice
|_______________________________________________| was
|___________________________________________| that
|____________________________________| as
|________________________________| her
|_____________________________| with
|_____________________________| at
|__________________________| on
|__________________________| all
|_______________________| this
|_______________________| for
|_______________________| had
|_______________________| but
|______________________| be
|_____________________| not
|____________________| they
|____________________| so
|___________________| very
|__________________| what

在极端情况下的替代实现示例:

 _______________________________________________________________
|_______________________________________________________________| she
|_______________________________________________________| superlongstringstring
|____________________________________________________| said
|______________________________________________| alice
|________________________________________| was
|_____________________________________| that
|_______________________________| as
|____________________________| her
|_________________________| with
|_________________________| at
|_______________________| on
|______________________| all
|____________________| this
|____________________| for
|____________________| had
|____________________| but
|___________________| be
|__________________| not
|_________________| they
|_________________| so
|________________| very
|________________| what

你可以通过将 is|in|it|i 折叠成 i[snt]? 来缩短停用词的正则表达式 - 然后可选规则就没有区别了。(嗯,我从来没有想过要告诉 Perl 的人如何使用正则表达式:D)- 现在唯一的问题是:我必须看看如何从自己的解决方案中削减三个字节,以再次超越 Perl :-| - Joey
好的,忽略我之前说的一部分。忽略单个字母的单词确实比不忽略要短一个字节。 - Joey
每个字节都很重要 ;) 我考虑过使用换行符的技巧,但我发现实际上它包含相同数量的字节,即使它包含更少的可打印字符。仍在努力看看能否进一步缩小它 :) - Syntaera
通过使用字符类和尽可能使用for而不是map,可以进一步简化代码: perl -E '$/=\0;/^(the|and|of|to|.|i[tns]|or)$/||$x{$_}++for split(/[^a-z]/,lc<>);map{$z=$x{$_};$y||{$y=(76-y///c)/$z}&&say" "."_"x($z*$y);say"|"."_"x($z*$y)."| $_"}sort{$x{$b}<=>$x{$a}}keys%x' - Chas. Owens
谢谢你 - 我得出了与for相同的结论,但是我摆脱了split(),而是使用了一个裸正则表达式。现在降到了203! - Syntaera
显示剩余3条评论

9

F#, 452个字符

简单明了:获取一个单词计数对的序列 a,找到最佳的单词计数每列乘数 k,然后打印结果。

let a=
 stdin.ReadToEnd().Split(" .?!,\":;'\r\n".ToCharArray(),enum 1)
 |>Seq.map(fun s->s.ToLower())|>Seq.countBy id
 |>Seq.filter(fun(w,n)->not(set["the";"and";"of";"to";"a";"i";"it";"in";"or";"is"].Contains w))
 |>Seq.sortBy(fun(w,n)-> -n)|>Seq.take 22
let k=a|>Seq.map(fun(w,n)->float(78-w.Length)/float n)|>Seq.min
let u n=String.replicate(int(float(n)*k)-2)"_"
printfn" %s "(u(snd(Seq.nth 0 a)))
for(w,n)in a do printfn"|%s| %s "(u n)w

示例(我的频率计数与您不同,不确定原因):

% app.exe < Alice.txt

 _________________________________________________________________________
|_________________________________________________________________________| she
|_______________________________________________________________| you
|_____________________________________________________________| said
|_____________________________________________________| alice
|_______________________________________________| was
|___________________________________________| that
|___________________________________| as
|________________________________| her
|_____________________________| with
|_____________________________| at
|____________________________| t
|____________________________| s
|__________________________| on
|_________________________| all
|_______________________| this
|______________________| had
|______________________| for
|_____________________| but
|_____________________| be
|____________________| not
|___________________| they
|__________________| so

结果发现我的解决方案确实有点偏差(由于规格略有不同),现在的解决方案是相应的;-) - ChristopheD
到目前为止,仅有一种正确的条形缩放实现。+1 - Rotsor
2
(@Rotsor:讽刺的是,我的解决方案是最古老的。) - Brian
我敢打赌,通过合并分裂、映射和过滤阶段,你可以将其缩短很多。我还预计你不需要那么多的浮点数。 - Gabe
嵌套函数通常不比使用管道操作符|>更短吗? - Joey

8

Python 2.6, 347 chars

import re
W,x={},"a and i in is it of or the to".split()
[W.__setitem__(w,W.get(w,0)-1)for w in re.findall("[a-z]+",file("11.txt").read().lower())if w not in x]
W=sorted(W.items(),key=lambda p:p[1])[:22]
bm=(76.-len(W[0][0]))/W[0][1]
U=lambda n:"_"*int(n*bm)
print "".join(("%s\n|%s| %s "%((""if i else" "+U(n)),U(n),w))for i,(w,n)in enumerate(W))

输出:

 _________________________________________________________________________
|_________________________________________________________________________| she 
|_______________________________________________________________| you 
|____________________________________________________________| said 
|_____________________________________________________| alice 
|_______________________________________________| was 
|___________________________________________| that 
|____________________________________| as 
|________________________________| her 
|_____________________________| with 
|_____________________________| at 
|____________________________| s 
|____________________________| t 
|__________________________| on 
|__________________________| all 
|_______________________| this 
|_______________________| for 
|_______________________| had 
|_______________________| but 
|______________________| be 
|_____________________| not 
|____________________| they 
|____________________| so 

1
你可以删除行 bm=(76.-len(W[0][0]))/W[0][1],因为你只使用了一次bm(将下一行改为U=lambda n:"_"*int(n*(76.-len(W[0][0]))/W[0][1]),可以减少5个字符)。另外:在代码高尔夫中,为什么要使用一个2个字符的变量名呢?;-) - ChristopheD
在最后一行,print后面的空格是不必要的,可以减少一个字符。 - ChristopheD
1
不考虑第二个最常见的单词非常长的情况,对吧? - Joey
@ChristopheD: 因为我盯着那段代码看了有点长时间。 :P 感谢你的发现。 @Johannes: 这也可以修复,没错。我写这个时,不确定所有其他实现都这么做。 - AKX

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接