Related
Can't encipher text in Ruby. It showes me the last letter of cipher-key in all my plaintext after iterating.
key is: VCHPRZGJNTLSKFBDQWAXEUYMOI
plaintext is: Hello, CS-50!
expected ciphered_text: Jrssb, HA-50!
I got ciphered_text: Iiiii, II-50!
I don't know why I got a last letter of a key (I) in every char of ciphered_text....
Maybe I need a "break" after every succes "if". But it doesn't helped.
Here is my code:
# Design and implement a program, substitution, that encrypts messages using a substitution cipher.
plaintext_str = 'Hello, CS-50!'
key_str = 'VCHPRZGJNTLSKFBDQWAXEUYMOI'
# Converting string into array:
plaintext = plaintext_str.split('')
key = key_str.split('')
# Check if letter is alphabetical
def alpha?(char)
char.match?(/^[[:alpha:]]$/)
end
# Check if letter is in uppercase
def upper?(char)
char.match?(/^[[:upper:]]$/)
end
# # Check if letter is in lowercase
def lower?(char)
char.match?(/^[[:lower:]]$/)
end
# ASCII arrays value assigned to capital letters for alphabets
capital_letters = [65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87,
88, 89, 90]
# ASCII arrays value assigned to small letters for alphabets
small_letters = [97,
98,
99,
100,
101,
102,
103,
104,
105,
106,
107,
108,
109,
110,
111,
112,
113,
114,
115,
116,
117,
118,
119,
120,
121,
122]
# Define variable for ciphertext:
ciphertext = ''
# iterating each plaintext's char (i-th):
plaintext.each_index do |i|
# iterating each key's char j-th on i-th char of plaintext:
key.each_index do |j|
# If char in plaintext is alphabetical:
if alpha?(plaintext[i])
# Check if letter is in uppercase
if upper?(plaintext[i])
capital_letters.each_index do |k|
# Checking if plaintext's letter is equal to alphabet's letter in [j]
if plaintext[i].ord == capital_letters[k]
ciphertext[i] = key[j].upcase
break
end
end
# Check if letter is in lowercase
elsif lower?(plaintext[i])
small_letters.each_index do |l|
if plaintext[i].ord == small_letters[l]
ciphertext[i] = key[j].downcase
break
end
end
end
# if non-alphabetical:
else
ciphertext[i] = plaintext[i]
end
end
end
puts "ciphertext: #{ciphertext}"
As a preface, the code you have here looks an awful lot like C. There are a lot of concepts that Ruby provides which can make problems like this into significantly shorter solutions. For now, we'll focus on what's wrong with your code as it is.
The main issue is you have three index variables, i from plaintext, j from key, and k from capital_letters. You check that if plaintext[i] == capital_letters[k], then you place key[j] at the ith position. But, since j was never participating in the checks, you will simply pass this check for all indices in your key variable. So, you might as well have ciphertext[i] = key.last.upcase, and if you check key, the last entry is I, hence why your output is nothing but I's.
Some suggestions for how you could simplify your code:
def encipher(plaintext, key)
# Make a table that maps { plaintext character => target character }
# With your input example, table = {"A" => "V", "B" => "C", "C" => "H", ... }
table = key.chars
.each_with_index
.map { |sub, index| [Array("A".."Z")[index], sub] }
.to_h
plaintext.map { |char|
# TODO: Using char and table, encipher exactly one character
}.join
end
I'm trying to send a parameter to a Ruby proc
p1 = [54, 21, 45, 76, 12, 11, 67, 5]
qualify = proc { |age, other| age > other }
puts p1.select(&qualify(30))
This is the error I get:
undefined method `qualify' for main:Object
age comes from the iteration of the array, and I want to have that last parameter (30) to get into the proc.
Is a proc the right tool to be using for this? I'm new to proc. I'm unclear how to get that parameter in there.
In order to use qualify in as select predicate, you need to reduce its arity (number of accepted arguments) through partial application. In other words - you need a new proc that would have other set to 30. It can be done with Method#curry, but it requires changing order of parameters:
qualify = proc { |other, age| age > other }
qualify.curry.call(30).call(10)
# => false
qualify.curry.call(30).call(40)
#=> true
I order to be able to pass this proc to select using &, you need to assign it so that it's available in the main object, e.g. by assigning it to an instance variable:
#qualify_30 = qualify.curry.call(30)
Now you can call:
p1.select{ |age| #qualify_30.call(age) }
# => [54, 45, 76, 67]
or:
p1.select(&#qualify_30)
# => [54, 45, 76, 67]
or inline:
p1.select(&qualify.curry.call(30))
# => [54, 45, 76, 67]
The easy way is to shuffle up how you define this:
p1 = [54, 21, 45, 76, 12, 11, 67, 5]
qualify = proc { |age| age > 30 }
puts p1.select(&qualify).join(',')
By moving the 30 into the qualify proc you've baked in the condition, it's no longer dynamic. Remember, the only methods that can be used with the shorthand &: trick are zero-argument ones, or single argument ones with & on a proc.
You could also use a closure to have the comparison variable exposed:
p1 = [54, 21, 45, 76, 12, 11, 67, 5]
required = 30
qualify = proc { |age| age > required }
puts p1.select(&qualify).join(',')
required = 10
puts p1.select(&qualify).join(',')
The better way is to just spell it out, that's what Ruby is all about. Here in a more idiomatic form:
p1 = [54, 21, 45, 76, 12, 11, 67, 5]
puts p1.select { |age| age > 30 }
The only reason for an intermediate Proc is if you'd want to, for some reason, save that somewhere and re-use it later.
Use the select statement in the proc itself, so that the proc would calculate and return an array.
2.1.5 :119 > qualify = proc { |age_array, age_limit| age_array.select { |age| age > age_limit } }
=> #<Proc:0xe7bc2cc#(irb):119>
2.1.5 :120 >
2.1.5 :121 >
2.1.5 :122 > qualify.call(p1, 30)
=> [54, 45, 76, 67]
Can someone explain why "time" is the max value here?
my_array = %w{hello my time here is long}
my_array.max #=> "time"
Because alphabetically t in time is greater here among others in your array my_array.
Here is one way,how string comparisons happened :
'hello' > 'time' # => false
'my' > 'time' # => false
'here' > 'time' # => false
'is' > 'time' # => false
'long' > 'time' # => false
To understand the outputs of the above fragment code,you must need to see String#<=> documentation. As your my_array contains all string instances,which has called the method <=>,to build the output of max.
Documentations says Enumerable#max:
Enumerable#max,without block assumes all objects implement Comparable.
Here's how computers look at the strings and compare them.
If we look at the first characters of each word it'll help a little, because we know how the alphabet orders letters:
%w[hello my time here is long].map{ |s| s[0] }.sort # => ["h", "h", "i", "l", "m", "t"]
But that doesn't really help visualize it, so here's a look at each word's letters as a computer sees them:
%w[time tome].each do |w|
puts w.chars.map(&:ord).join(', ')
end
# >> 116, 105, 109, 101
# >> 116, 111, 109, 101
Each letter has a value. Over the years there have been many different ways of ordering letters for a computer, which caused the character to value mapping to change. EBCDIC and ASCII have been the most popular but have different orders. We're usually dealing with ASCII, or a derivative, which is set by the OS.
Look at how the characters in the words are represented by the values in the following output. It should make it easy to understand what the computer is doing then.
%w[he hello help holler hollow].sort.each do |w|
puts '"%6s": %s' % [ w, w.chars.map(&:ord).join(', ') ]
end
# >> " he": 104, 101
# >> " hello": 104, 101, 108, 108, 111
# >> " help": 104, 101, 108, 112
# >> "holler": 104, 111, 108, 108, 101, 114
# >> "hollow": 104, 111, 108, 108, 111, 119
When reading file names from Ruby 1.9.3, I'm seeing some odd results. For example with the following test ruby script, running in a folder containing a file with the name 'Testé.txt'
#!encoding:UTF-8
def inspect_string s
puts "Source encoding: #{"".encoding}"
puts "External encoding: #{Encoding.default_external}"
puts "Name: #{s.inspect}"
puts "Encoding: #{s.encoding}"
puts "Chars: #{s.chars.to_a.inspect}"
puts "Codepoints: #{s.codepoints.to_a.inspect}"
puts "Bytes: #{s.bytes.to_a.inspect}"
end
def transform_string s
puts "Testing string #{s}"
puts s.gsub(/é/u,'TEST')
end
Dir.glob("./*.txt").each do |f|
puts RUBY_VERSION + RUBY_PLATFORM
puts "Inline string works as expected"
s = "./Testé.txt"
inspect_string s
puts transform_string s
puts "File name from Dir.glob does not"
inspect_string f
puts transform_string f
end
On Mac OS X Lion, I see the following results:
1.9.3x86_64-darwin11.4.0
Inline string works as expected
Source encoding: UTF-8
External encoding: UTF-8
Name: "./Testé.txt"
Encoding: UTF-8
Chars: [".", "/", "T", "e", "s", "t", "é", ".", "t", "x", "t"]
Codepoints: [46, 47, 84, 101, 115, 116, 233, 46, 116, 120, 116]
Bytes: [46, 47, 84, 101, 115, 116, 195, 169, 46, 116, 120, 116]
Testing string ./Testé.txt
./TestTEST.txt
File name from Dir.glob does not
Source encoding: UTF-8
External encoding: UTF-8
Name: "./Testé.txt"
Encoding: UTF-8
Chars: [".", "/", "T", "e", "s", "t", "e", "́", ".", "t", "x", "t"]
Codepoints: [46, 47, 84, 101, 115, 116, 101, 769, 46, 116, 120, 116]
Bytes: [46, 47, 84, 101, 115, 116, 101, 204, 129, 46, 116, 120, 116]
Testing string ./Testé.txt
./Testé.txt
The expected last line is
./TestTEST.txt
the encodings returned indicate that this is a normal UTF-8 string and yet any regexp transformations involving unicode are not being applied properly.
An update to this: Ruby 2.2.0 has gained String#unicode_normalize.
f.unicode_normalize!
would convert the NFD-decomposed string returned from OSX' HFS+ filesystem into a NFC-composed string. You can specify :nfd, :nfkc, or :nfkd if you require alternative normalizations.
Posted in case this is useful for anyone else running into this:
Ruby 1.9 and 2.0 will use composed UTF-8 strings if you use UTF-8 encoding, but will not modify strings received from the OS. Mac OS X uses decomposed strings (two bytes for many common accents like é in UTF-8, which are combined for display). So file system methods will often return unexpected string formats, which are strictly UTF-8, but a decomposed form.
In order to work around this, you need to decompose them by converting from the 'UTF8-MAC' encoding to UTF-8:
f.encode!('UTF-8','UTF8-MAC')
Before using them, otherwise you may end up running checks against a decomposed string with a native ruby string which is composed.
This behaviour affects all file system calls like glob for both files and folders where a file name contains unicode characters.
Apple docs:
http://developer.apple.com/library/mac/#qa/qa1235/_index.html
this wiki page gave a general idea of how to convert a single char to ascii http://en.wikibooks.org/wiki/Ruby_Programming/ASCII
But say if I have a string and I wanted to get each character's ascii from it, what do i need to do?
"string".each_byte do |c|
$char = c.chr
$ascii = ?char
puts $ascii
end
It doesn't work because it's not happy with the line $ascii = ?char
syntax error, unexpected '?'
$ascii = ?char
^
The c variable already contains the char code!
"string".each_byte do |c|
puts c
end
yields
115
116
114
105
110
103
puts "string".split('').map(&:ord).to_s
Ruby String provides the codepoints method after 1.9.1.
str = 'hello world'
str.codepoints
=> [104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]
str = "你好世界"
str.codepoints
=> [20320, 22909, 19990, 30028]
use "x".ord for a single character or "xyz".sum for a whole string.
please refer to this post for the changes in ruby1.9 Getting an ASCII character code in Ruby using `?` (question mark) fails
You could also just call to_a after each_byte or even better String#bytes
=> 'hello world'.each_byte.to_a
=> [104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]
=> 'hello world'.bytes
=> [104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]
"a"[0]
or
?a
Both would return their ASCII equivalent.