Full text of the problem
Using names.txt (right click and 'Save Link/Target As...'), a 46K text file containing over five-thousand first names, begin by sorting it into alphabetical order. Then working out the alphabetical value for each name, multiply this value by its alphabetical position in the list to obtain a name score.
For example, when the list is sorted into alphabetical order, COLIN, which is worth 3 +たす 15 +たす 12 +たす 9 +たす 14 =わ 53, is the 938th name in the list. So, COLIN would obtain a score of 938 ×ばつかける 53 =わ 49714.
What is the total of all the name scores in the file?
I thought that a flowchart for Project Euler 22 would look nice, so I shook off the dust from my automatic flowchart generator (and made small modifications to it) and wrote a small script. I include the flowchart generator for convenience:
DEBUGGING = 1
class Object
def log(previous_method='')
if not DEBUGGING
return
end
repr = if self.class == Enumerator then self.to_a else self end
if [Array, String].include?(repr.class) and repr.length > 100
if repr.class == String
repr = repr.split('') end
repr = repr.first(30) + [". . ."] + repr.last(30)
if self.class == String
repr = repr.join end
end
if previous_method.downcase == 'start'
self.tap {|x| puts """
#{repr}
"""}
else
self.tap {|x| puts """
|
| #{previous_method}
|
V
#{repr}
"""}
end
end
end
def word_value(word)
alphabet = ' ABCDEFGHIJKLMNOPQRSTUVWXYZ'
word.split('') #.log
.map{|ch| alphabet.index(ch)} #.log
.inject(&:+) #.log
end
File.read('eul.txt') .log("Readfile")
.split(",") .log("Split on comma")
.map{|w| w[1..-2]} .log("Remove quotes")
.sort .log("Sort lexicographically")
.map {|w| word_value(w)} .log("Sum of the alphabetical positions of tha chars")
.each_with_index .log("Insert indexes")
.map{|value, pos| value * (pos + 1)} .log("Multiply word value by position")
.inject(&:+) .log("Sum all")
The flowchart is:
|
| Readfile
|
V
"MARY","PATRICIA","LINDA","BAR. . .,"DARELL","BRODERICK","ALONSO
|
| Split on comma
|
V
["\"MARY\"", "\"PATRICIA\"", "\"LINDA\"", "\"BARBARA\"", "\"ELIZABETH\"", "\"JENNIFER\"", "\"MARIA\"", "\"SUSAN\"", "\"MARGARET\"", "\"DOROTHY\"", "\"LISA\"", "\"NANCY\"", "\"KAREN\"", "\"BETTY\"", "\"HELEN\"", "\"SANDRA\"", "\"DONNA\"", "\"CAROL\"", "\"RUTH\"", "\"SHARON\"", "\"MICHELLE\"", "\"LAURA\"", "\"SARAH\"", "\"KIMBERLY\"", "\"DEBORAH\"", "\"JESSICA\"", "\"SHIRLEY\"", "\"CYNTHIA\"", "\"ANGELA\"", "\"MELISSA\"", ". . .", "\"KRAIG\"", "\"JERRELL\"", "\"JEROMY\"", "\"HOBERT\"", "\"CEDRICK\"", "\"ARLIE\"", "\"WINFORD\"", "\"WALLY\"", "\"LUIGI\"", "\"KENETH\"", "\"JACINTO\"", "\"GRAIG\"", "\"FRANKLYN\"", "\"EDMUNDO\"", "\"SID\"", "\"PORTER\"", "\"LEIF\"", "\"JERAMY\"", "\"BUCK\"", "\"WILLIAN\"", "\"VINCENZO\"", "\"SHON\"", "\"LYNWOOD\"", "\"JERE\"", "\"HAI\"", "\"ELDEN\"", "\"DORSEY\"", "\"DARELL\"", "\"BRODERICK\"", "\"ALONSO\n"]
|
| Remove quotes
|
V
["MARY", "PATRICIA", "LINDA", "BARBARA", "ELIZABETH", "JENNIFER", "MARIA", "SUSAN", "MARGARET", "DOROTHY", "LISA", "NANCY", "KAREN", "BETTY", "HELEN", "SANDRA", "DONNA", "CAROL", "RUTH", "SHARON", "MICHELLE", "LAURA", "SARAH", "KIMBERLY", "DEBORAH", "JESSICA", "SHIRLEY", "CYNTHIA", "ANGELA", "MELISSA", ". . .", "KRAIG", "JERRELL", "JEROMY", "HOBERT", "CEDRICK", "ARLIE", "WINFORD", "WALLY", "LUIGI", "KENETH", "JACINTO", "GRAIG", "FRANKLYN", "EDMUNDO", "SID", "PORTER", "LEIF", "JERAMY", "BUCK", "WILLIAN", "VINCENZO", "SHON", "LYNWOOD", "JERE", "HAI", "ELDEN", "DORSEY", "DARELL", "BRODERICK", "ALONSO"]
|
| Sort lexicographically
|
V
["AARON", "ABBEY", "ABBIE", "ABBY", "ABDUL", "ABE", "ABEL", "ABIGAIL", "ABRAHAM", "ABRAM", "ADA", "ADAH", "ADALBERTO", "ADALINE", "ADAM", "ADAN", "ADDIE", "ADELA", "ADELAIDA", "ADELAIDE", "ADELE", "ADELIA", "ADELINA", "ADELINE", "ADELL", "ADELLA", "ADELLE", "ADENA", "ADINA", "ADOLFO", ". . .", "ZACHARY", "ZACHERY", "ZACK", "ZACKARY", "ZADA", "ZAIDA", "ZANA", "ZANDRA", "ZANE", "ZELDA", "ZELLA", "ZELMA", "ZENA", "ZENAIDA", "ZENIA", "ZENOBIA", "ZETTA", "ZINA", "ZITA", "ZOE", "ZOFIA", "ZOILA", "ZOLA", "ZONA", "ZONIA", "ZORA", "ZORAIDA", "ZULA", "ZULEMA", "ZULMA"]
|
| Sum of the alphabetical positions of tha chars
|
V
[49, 35, 19, 30, 40, 8, 20, 41, 44, 35, 6, 14, 78, 46, 19, 20, 23, 23, 37, 41, 27, 32, 46, 50, 34, 35, 39, 25, 29, 53, ". . .", 82, 86, 41, 85, 32, 41, 42, 64, 46, 48, 56, 57, 46, 60, 55, 72, 72, 50, 56, 46, 57, 63, 54, 56, 65, 60, 74, 60, 78, 73]
|
| Insert indexes
|
V
[[49, 0], [35, 1], [19, 2], [30, 3], [40, 4], [8, 5], [20, 6], [41, 7], [44, 8], [35, 9], [6, 10], [14, 11], [78, 12], [46, 13], [19, 14], [20, 15], [23, 16], [23, 17], [37, 18], [41, 19], [27, 20], [32, 21], [46, 22], [50, 23], [34, 24], [35, 25], [39, 26], [25, 27], [29, 28], [53, 29], ". . .", [82, 5133], [86, 5134], [41, 5135], [85, 5136], [32, 5137], [41, 5138], [42, 5139], [64, 5140], [46, 5141], [48, 5142], [56, 5143], [57, 5144], [46, 5145], [60, 5146], [55, 5147], [72, 5148], [72, 5149], [50, 5150], [56, 5151], [46, 5152], [57, 5153], [63, 5154], [54, 5155], [56, 5156], [65, 5157], [60, 5158], [74, 5159], [60, 5160], [78, 5161], [73, 5162]]
|
| Multiply word value by position
|
V
[49, 70, 57, 120, 200, 48, 140, 328, 396, 350, 66, 168, 1014, 644, 285, 320, 391, 414, 703, 820, 567, 704, 1058, 1200, 850, 910, 1053, 700, 841, 1590, ". . .", 420988, 441610, 210576, 436645, 164416, 210699, 215880, 329024, 236532, 246864, 288064, 293265, 236716, 308820, 283140, 370728, 370800, 257550, 288512, 237038, 293778, 324765, 278424, 288792, 335270, 309540, 381840, 309660, 402636, 376899]
|
| Sum all
|
V
871198282
-
\$\begingroup\$ Please include a summary of the challenge being solved. \$\endgroup\$200_success– 200_success2015年05月12日 05:49:15 +00:00Commented May 12, 2015 at 5:49
1 Answer 1
In the following, I'll refer to Rubocop's Ruby Style Guide a lot, simply because it's pretty comprehensive, and mostly has me nodding in agreement.
2 spaces of indentation, please
Use ternaries rather than same-line
if... then... else... end
Don't use
and
/or
, and avoidnot
.Please don't do this:
if self.class == String repr = repr.join end
Instead, postfix the condition:
repr = repr.join if self.class == String
All that being said, Ruby is flexible, and your style obviously works. But because of that flexibility Ruby coders are also really big on conventions and keeping style consistent. There's a pretty unified and fixed notion of what's the "correct" style to use, and the style guide I linked to is a good reference. You're free to disagree of course, just know that you'll probably be going against the opinions of the vast majority of Rubyists.
As for the code itself:
Better to use
Object#kind_of?
than comparing class to class directly, as#kind_of?
will also match a subclass against its super-classes. Presuming your objects adhere to the Liskov substitution principle (as they certainly should), it's the right thing to do.But alternatively, you could extend
String
andArray
with methods to return truncated descriptions. That way, your#log
method won't have to mess around with class checks at all. Call the method, say,#truncated_description
because there's no reason to be cryptic. ForArray
it might be:class Array def truncated_description return inspect if count <= 40 (first(20) + ['...'] + last(20)).inspect end end
You can do something similar for
String
or perhapsEnumerable
. Point is, it'll let you get the description to log by sayingobj.respond_to?(:truncated_description) ? obj.truncated_description : obj.to_s
I'm not a fan of
repr
. I imagine it's short forrepresentation
, but overly-shortened variable names isn't very Rubyesque, and it took me a moment too long to figure out what it might be.The
#log
method is pretty long for a Ruby method. Some of it is multiline strings, true, but still. Aim for short methods. Like, really short ones. It helps/forces you to factor your code into methods that do one thing, and only one thing. Of course, you can't always get away with it, and here, where you're extending an existing class, you don't want to pollute that class with tons of methods. I consider that last point a good reason to condone longer methods. Still, you can save some lines.You don't need
#tap
; you're already extendingObject
, so you can just returnself
from your method, and it's functionally the same.However, I'd be tempted to not extend
Object
at all (it's a pretty big deal to monkeypatch the base class, and not to done lightly), and instead use#tap
to send stuff on to a logging method. Or even a proc. E.g.:flow_logger = -> (obj, message = nil) do if message puts <<-END.gsub(/^\s*\|/, '') | | | | | #{message} | | | V | END end p obj end (0...10).tap { |obj| flow_logger[obj] } .to_a.tap { |obj| flow_logger[obj, "to_a"] } .map { |n| n % 3 == 0 }.tap { |obj| flow_logger[obj, "divisible by 3"] } .map(&:to_s).tap { |obj| flow_logger[obj, "to_s"] } .sort.tap { |obj| flow_logger[obj, "sort the strings"] } .join.tap { |obj| flow_logger[obj, "join 'em"] }
Obviously, there's a lot of duplicated code there, but it's still an alternative to monkeypatching
Object
.Note that in the above: a) I'm using
p
which implicitly calls#inspect
on its arguments, rather thanputs
or string interpolation, which calls#to_s
, and b) I'm using heredocs, and c) I'm trimming the heredoc output.
There are more things I could suggest, and just plain many ways to do things. But I'll stop here. Hopefully you can use the above for something.