
After go1.16, go will use module mode by default, even when the repository is checked out under GOPATH or in a one-off directory. Add go.mod, go.sum to keep this repo buildable without opting out of the module mode. > go mod init github.com/mmcgrana/gobyexample > go mod tidy > go mod vendor In module mode, the 'vendor' directory is special and its contents will be actively maintained by the go command. pygments aren't the dependency the go will know about, so it will delete the contents from vendor directory. Move it to `third_party` directory now. And, vendor the blackfriday package. Note: the tutorial contents are not affected by the change in go1.16 because all the examples in this tutorial ask users to run the go command with the explicit list of files to be compiled (e.g. `go run hello-world.go` or `go build command-line-arguments.go`). When the source list is provided, the go command does not have to compute the build list and whether it's running in GOPATH mode or module mode becomes irrelevant.
1224 lines
28 KiB
Ruby
1224 lines
28 KiB
Ruby
# -*- ruby -*-
|
|
|
|
# Local variables:
|
|
# indent-tabs-mode: nil
|
|
# ruby-indent-level: 4
|
|
# End:
|
|
|
|
# @@PLEAC@@_NAME
|
|
# @@SKIP@@ Ruby
|
|
|
|
# @@PLEAC@@_WEB
|
|
# @@SKIP@@ http://www.ruby-lang.org
|
|
|
|
|
|
# @@PLEAC@@_1.0
|
|
string = '\n' # two characters, \ and an n
|
|
string = 'Jon \'Maddog\' Orwant' # literal single quotes
|
|
|
|
string = "\n" # a "newline" character
|
|
string = "Jon \"Maddog\" Orwant" # literal double quotes
|
|
|
|
string = %q/Jon 'Maddog' Orwant/ # literal single quotes
|
|
|
|
string = %q[Jon 'Maddog' Orwant] # literal single quotes
|
|
string = %q{Jon 'Maddog' Orwant} # literal single quotes
|
|
string = %q(Jon 'Maddog' Orwant) # literal single quotes
|
|
string = %q<Jon 'Maddog' Orwant> # literal single quotes
|
|
|
|
a = <<"EOF"
|
|
This is a multiline here document
|
|
terminated by EOF on a line by itself
|
|
EOF
|
|
|
|
|
|
# @@PLEAC@@_1.1
|
|
value = string[offset,count]
|
|
value = string[offset..-1]
|
|
|
|
string[offset,count] = newstring
|
|
string[offset..-1] = newtail
|
|
|
|
# in Ruby we can also specify intervals by their two offsets
|
|
value = string[offset..offs2]
|
|
string[offset..offs2] = newstring
|
|
|
|
leading, s1, s2, trailing = data.unpack("A5 x3 A8 A8 A*")
|
|
|
|
fivers = string.unpack("A5" * (string.length/5))
|
|
|
|
chars = string.unpack("A1" * string.length)
|
|
|
|
string = "This is what you have"
|
|
# +012345678901234567890 Indexing forwards (left to right)
|
|
# 109876543210987654321- Indexing backwards (right to left)
|
|
# note that 0 means 10 or 20, etc. above
|
|
|
|
first = string[0, 1] # "T"
|
|
start = string[5, 2] # "is"
|
|
rest = string[13..-1] # "you have"
|
|
last = string[-1, 1] # "e"
|
|
end_ = string[-4..-1] # "have"
|
|
piece = string[-8, 3] # "you"
|
|
|
|
string[5, 2] = "wasn't" # change "is" to "wasn't"
|
|
string[-12..-1] = "ondrous" # "This wasn't wondrous"
|
|
string[0, 1] = "" # delete first character
|
|
string[-10..-1] = "" # delete last 10 characters
|
|
|
|
if string[-10..-1] =~ /pattern/
|
|
puts "Pattern matches in last 10 characters"
|
|
end
|
|
|
|
string[0, 5].gsub!(/is/, 'at')
|
|
|
|
a = "make a hat"
|
|
a[0, 1], a[-1, 1] = a[-1, 1], a[0, 1]
|
|
|
|
a = "To be or not to be"
|
|
b = a.unpack("x6 A6")
|
|
|
|
b, c = a.unpack("x6 A2 X5 A2")
|
|
puts "#{b}\n#{c}\n"
|
|
|
|
def cut2fmt(*args)
|
|
template = ''
|
|
lastpos = 1
|
|
for place in args
|
|
template += "A" + (place - lastpos).to_s + " "
|
|
lastpos = place
|
|
end
|
|
template += "A*"
|
|
return template
|
|
end
|
|
|
|
fmt = cut2fmt(8, 14, 20, 26, 30)
|
|
|
|
|
|
# @@PLEAC@@_1.2
|
|
# careful! "b is true" doesn't mean "b != 0" (0 is true in Ruby)
|
|
# thus no problem of "defined" later since only nil is false
|
|
# the following sets to `c' if `b' is nil or false
|
|
a = b || c
|
|
|
|
# if you need Perl's behaviour (setting to `c' if `b' is 0) the most
|
|
# effective way is to use Numeric#nonzero? (thanks to Dave Thomas!)
|
|
a = b.nonzero? || c
|
|
|
|
# you will still want to use defined? in order to test
|
|
# for scope existence of a given object
|
|
a = defined?(b) ? b : c
|
|
|
|
dir = ARGV.shift || "/tmp"
|
|
|
|
|
|
# @@PLEAC@@_1.3
|
|
v1, v2 = v2, v1
|
|
|
|
alpha, beta, production = %w(January March August)
|
|
alpha, beta, production = beta, production, alpha
|
|
|
|
|
|
# @@PLEAC@@_1.4
|
|
num = char[0]
|
|
char = num.chr
|
|
|
|
# Ruby also supports having a char from character constant
|
|
num = ?r
|
|
|
|
char = sprintf("%c", num)
|
|
printf("Number %d is character %c\n", num, num)
|
|
|
|
ascii = string.unpack("C*")
|
|
string = ascii.pack("C*")
|
|
|
|
hal = "HAL"
|
|
ascii = hal.unpack("C*")
|
|
# We can't use Array#each since we can't mutate a Fixnum
|
|
ascii.collect! { |i|
|
|
i + 1 # add one to each ASCII value
|
|
}
|
|
ibm = ascii.pack("C*")
|
|
puts ibm
|
|
|
|
|
|
# @@PLEAC@@_1.5
|
|
array = string.split('')
|
|
|
|
array = string.unpack("C*")
|
|
|
|
string.scan(/./) { |b|
|
|
# do something with b
|
|
}
|
|
|
|
string = "an apple a day"
|
|
print "unique chars are: ", string.split('').uniq.sort, "\n"
|
|
|
|
sum = 0
|
|
for ascval in string.unpack("C*") # or use Array#each for a pure OO style :)
|
|
sum += ascval
|
|
end
|
|
puts "sum is #{sum & 0xffffffff}" # since Ruby will go Bignum if necessary
|
|
|
|
# @@INCLUDE@@ include/ruby/slowcat.rb
|
|
|
|
|
|
# @@PLEAC@@_1.6
|
|
revbytes = string.reverse
|
|
|
|
revwords = string.split(" ").reverse.join(" ")
|
|
|
|
revwords = string.split(/(\s+)/).reverse.join
|
|
|
|
# using the fact that IO is Enumerable, you can directly "select" it
|
|
long_palindromes = File.open("/usr/share/dict/words").
|
|
select { |w| w.chomp!; w.reverse == w && w.length > 5 }
|
|
|
|
|
|
# @@PLEAC@@_1.7
|
|
while string.sub!("\t+") { ' ' * ($&.length * 8 - $`.length % 8) }
|
|
end
|
|
|
|
|
|
# @@PLEAC@@_1.8
|
|
'You owe #{debt} to me'.gsub(/\#{(\w+)}/) { eval($1) }
|
|
|
|
rows, cols = 24, 80
|
|
text = %q(I am #{rows} high and #{cols} long)
|
|
text.gsub!(/\#{(\w+)}/) { eval("#{$1}") }
|
|
puts text
|
|
|
|
'I am 17 years old'.gsub(/\d+/) { 2 * $&.to_i }
|
|
|
|
|
|
# @@PLEAC@@_1.9
|
|
e = "bo peep".upcase
|
|
e.downcase!
|
|
e.capitalize!
|
|
|
|
"thIS is a loNG liNE".gsub!(/\w+/) { $&.capitalize }
|
|
|
|
|
|
# @@PLEAC@@_1.10
|
|
"I have #{n+1} guanacos."
|
|
print "I have ", n+1, " guanacos."
|
|
|
|
|
|
# @@PLEAC@@_1.11
|
|
var = <<'EOF'.gsub(/^\s+/, '')
|
|
your text
|
|
goes here
|
|
EOF
|
|
|
|
|
|
# @@PLEAC@@_1.12
|
|
string = "Folding and splicing is the work of an editor,\n"+
|
|
"not a mere collection of silicon\n"+
|
|
"and\n"+
|
|
"mobile electrons!"
|
|
|
|
def wrap(str, max_size)
|
|
all = []
|
|
line = ''
|
|
for l in str.split
|
|
if (line+l).length >= max_size
|
|
all.push(line)
|
|
line = ''
|
|
end
|
|
line += line == '' ? l : ' ' + l
|
|
end
|
|
all.push(line).join("\n")
|
|
end
|
|
|
|
print wrap(string, 20)
|
|
#=> Folding and
|
|
#=> splicing is the
|
|
#=> work of an editor,
|
|
#=> not a mere
|
|
#=> collection of
|
|
#=> silicon and mobile
|
|
#=> electrons!
|
|
|
|
|
|
# @@PLEAC@@_1.13
|
|
string = %q(Mom said, "Don't do that.")
|
|
string.gsub(/['"]/) { '\\'+$& }
|
|
string.gsub(/['"]/, '\&\&')
|
|
string.gsub(/[^A-Z]/) { '\\'+$& }
|
|
"is a test!".gsub(/\W/) { '\\'+$& } # no function like quotemeta?
|
|
|
|
|
|
# @@PLEAC@@_1.14
|
|
string.strip!
|
|
|
|
|
|
# @@PLEAC@@_1.15
|
|
def parse_csv(text)
|
|
new = text.scan(/"([^\"\\]*(?:\\.[^\"\\]*)*)",?|([^,]+),?|,/)
|
|
new << nil if text[-1] == ?,
|
|
new.flatten.compact
|
|
end
|
|
|
|
line = %q<XYZZY,"","O'Reilly, Inc","Wall, Larry","a \"glug\" bit,",5,"Error, Core Dumped">
|
|
fields = parse_csv(line)
|
|
fields.each_with_index { |v,i|
|
|
print "#{i} : #{v}\n";
|
|
}
|
|
|
|
|
|
# @@PLEAC@@_1.16
|
|
# Use the soundex.rb Library from Michael Neumann.
|
|
# http://www.s-direktnet.de/homepages/neumann/rb_prgs/Soundex.rb
|
|
require 'Soundex'
|
|
|
|
code = Text::Soundex.soundex(string)
|
|
codes = Text::Soundex.soundex(array)
|
|
|
|
# substitution function for getpwent():
|
|
# returns an array of user entries,
|
|
# each entry contains the username and the full name
|
|
def login_names
|
|
result = []
|
|
File.open("/etc/passwd") { |file|
|
|
file.each_line { |line|
|
|
next if line.match(/^#/)
|
|
cols = line.split(":")
|
|
result.push([cols[0], cols[4]])
|
|
}
|
|
}
|
|
result
|
|
end
|
|
|
|
puts "Lookup user: "
|
|
user = STDIN.gets
|
|
user.chomp!
|
|
exit unless user
|
|
name_code = Text::Soundex.soundex(user)
|
|
|
|
splitter = Regexp.new('(\w+)[^,]*\b(\w+)')
|
|
for username, fullname in login_names do
|
|
firstname, lastname = splitter.match(fullname)[1,2]
|
|
if name_code == Text::Soundex.soundex(username)
|
|
|| name_code == Text::Soundex.soundex(firstname)
|
|
|| name_code == Text::Soundex.soundex(lastname)
|
|
then
|
|
puts "#{username}: #{firstname} #{lastname}"
|
|
end
|
|
end
|
|
|
|
|
|
# @@PLEAC@@_1.17
|
|
# @@INCLUDE@@ include/ruby/fixstyle.rb
|
|
|
|
|
|
# @@PLEAC@@_1.18
|
|
# @@INCLUDE@@ include/ruby/psgrep.rb
|
|
|
|
|
|
# @@PLEAC@@_2.1
|
|
# Matz tells that you can use Integer() for strict checked conversion.
|
|
Integer("abc")
|
|
#=> `Integer': invalid value for Integer: "abc" (ArgumentError)
|
|
Integer("567")
|
|
#=> 567
|
|
|
|
# You may use Float() for floating point stuff
|
|
Integer("56.7")
|
|
#=> `Integer': invalid value for Integer: "56.7" (ArgumentError)
|
|
Float("56.7")
|
|
#=> 56.7
|
|
|
|
# You may also use a regexp for that
|
|
if string =~ /^[+-]?\d+$/
|
|
p 'is an integer'
|
|
else
|
|
p 'is not'
|
|
end
|
|
|
|
if string =~ /^-?(?:\d+(?:\.\d*)?|\.\d+)$/
|
|
p 'is a decimal number'
|
|
else
|
|
p 'is not'
|
|
end
|
|
|
|
|
|
# @@PLEAC@@_2.2
|
|
# equal(num1, num2, accuracy) : returns true if num1 and num2 are
|
|
# equal to accuracy number of decimal places
|
|
def equal(i, j, a)
|
|
sprintf("%.#{a}g", i) == sprintf("%.#{a}g", j)
|
|
end
|
|
|
|
wage = 536 # $5.36/hour
|
|
week = 40 * wage # $214.40
|
|
printf("One week's wage is: \$%.2f\n", week/100.0)
|
|
|
|
|
|
# @@PLEAC@@_2.3
|
|
num.round # rounds to integer
|
|
|
|
a = 0.255
|
|
b = sprintf("%.2f", a)
|
|
print "Unrounded: #{a}\nRounded: #{b}\n"
|
|
printf "Unrounded: #{a}\nRounded: %.2f\n", a
|
|
|
|
print "number\tint\tfloor\tceil\n"
|
|
a = [ 3.3 , 3.5 , 3.7, -3.3 ]
|
|
for n in a
|
|
printf("% .1f\t% .1f\t% .1f\t% .1f\n", # at least I don't fake my output :)
|
|
n, n.to_i, n.floor, n.ceil)
|
|
end
|
|
|
|
|
|
# @@PLEAC@@_2.4
|
|
def dec2bin(n)
|
|
[n].pack("N").unpack("B32")[0].sub(/^0+(?=\d)/, '')
|
|
end
|
|
|
|
def bin2dec(n)
|
|
[("0"*32+n.to_s)[-32..-1]].pack("B32").unpack("N")[0]
|
|
end
|
|
|
|
|
|
# @@PLEAC@@_2.5
|
|
for i in x .. y
|
|
# i is set to every integer from x to y, inclusive
|
|
end
|
|
|
|
x.step(y,7) { |i|
|
|
# i is set to every integer from x to y, stepsize = 7
|
|
}
|
|
|
|
print "Infancy is: "
|
|
(0..2).each { |i|
|
|
print i, " "
|
|
}
|
|
print "\n"
|
|
|
|
|
|
# @@PLEAC@@_2.6
|
|
# We can add conversion methods to the Integer class,
|
|
# this makes a roman number just a representation for normal numbers.
|
|
class Integer
|
|
|
|
@@romanlist = [["M", 1000],
|
|
["CM", 900],
|
|
["D", 500],
|
|
["CD", 400],
|
|
["C", 100],
|
|
["XC", 90],
|
|
["L", 50],
|
|
["XL", 40],
|
|
["X", 10],
|
|
["IX", 9],
|
|
["V", 5],
|
|
["IV", 4],
|
|
["I", 1]]
|
|
|
|
def to_roman
|
|
remains = self
|
|
roman = ""
|
|
for sym, num in @@romanlist
|
|
while remains >= num
|
|
remains -= num
|
|
roman << sym
|
|
end
|
|
end
|
|
roman
|
|
end
|
|
|
|
def Integer.from_roman(roman)
|
|
ustr = roman.upcase
|
|
sum = 0
|
|
for entry in @@romanlist
|
|
sym, num = entry[0], entry[1]
|
|
while sym == ustr[0, sym.length]
|
|
sum += num
|
|
ustr.slice!(0, sym.length)
|
|
end
|
|
end
|
|
sum
|
|
end
|
|
|
|
end
|
|
|
|
|
|
roman_fifteen = 15.to_roman
|
|
puts "Roman for fifteen is #{roman_fifteen}"
|
|
i = Integer.from_roman(roman_fifteen)
|
|
puts "Converted back, #{roman_fifteen} is #{i}"
|
|
|
|
# check
|
|
for i in (1..3900)
|
|
r = i.to_roman
|
|
j = Integer.from_roman(r)
|
|
if i != j
|
|
puts "error: #{i} : #{r} - #{j}"
|
|
end
|
|
end
|
|
|
|
|
|
# @@PLEAC@@_2.7
|
|
random = rand(y-x+1)+x
|
|
|
|
chars = ["A".."Z","a".."z","0".."9"].collect { |r| r.to_a }.join + %q(!@$%^&*)
|
|
password = (1..8).collect { chars[rand(chars.size)] }.pack("C*")
|
|
|
|
|
|
# @@PLEAC@@_2.8
|
|
srand # uses a combination of the time, the process id, and a sequence number
|
|
srand(val) # for repeatable behaviour
|
|
|
|
|
|
# @@PLEAC@@_2.9
|
|
# from the randomr lib:
|
|
# http://raa.ruby-lang.org/project/randomr/
|
|
----> http://raa.ruby-lang.org/project/randomr/
|
|
|
|
require 'random/mersenne_twister'
|
|
mers = Random::MersenneTwister.new 123456789
|
|
puts mers.rand(0) # 0.550321932544541
|
|
puts mers.rand(10) # 2
|
|
|
|
# using online sources of random data via the realrand package:
|
|
# http://raa.ruby-lang.org/project/realrand/
|
|
# **Note**
|
|
# The following online services are used in this package:
|
|
# http://www.random.org - source: atmospheric noise
|
|
# http://www.fourmilab.ch/hotbits - source: radioactive decay timings
|
|
# http://random.hd.org - source: entropy from local and network noise
|
|
# Please visit the sites and respect the rules of each service.
|
|
|
|
require 'random/online'
|
|
|
|
generator1 = Random::RandomOrg.new
|
|
puts generator1.randbyte(5).join(",")
|
|
puts generator1.randnum(10, 1, 6).join(",") # Roll dice 10 times.
|
|
|
|
generator2 = Random::FourmiLab.new
|
|
puts generator2.randbyte(5).join(",")
|
|
# randnum is not supported.
|
|
|
|
generator3 = Random::EntropyPool.new
|
|
puts generator3.randbyte(5).join(",")
|
|
# randnum is not supported.
|
|
|
|
|
|
# @@PLEAC@@_2.10
|
|
def gaussian_rand
|
|
begin
|
|
u1 = 2 * rand() - 1
|
|
u2 = 2 * rand() - 1
|
|
w = u1*u1 + u2*u2
|
|
end while (w >= 1)
|
|
w = Math.sqrt((-2*Math.log(w))/w)
|
|
[ u2*w, u1*w ]
|
|
end
|
|
|
|
mean = 25
|
|
sdev = 2
|
|
salary = gaussian_rand[0] * sdev + mean
|
|
printf("You have been hired at \$%.2f\n", salary)
|
|
|
|
|
|
# @@PLEAC@@_2.11
|
|
def deg2rad(d)
|
|
(d/180.0)*Math::PI
|
|
end
|
|
|
|
def rad2deg(r)
|
|
(r/Math::PI)*180
|
|
end
|
|
|
|
|
|
# @@PLEAC@@_2.12
|
|
sin_val = Math.sin(angle)
|
|
cos_val = Math.cos(angle)
|
|
tan_val = Math.tan(angle)
|
|
|
|
# AFAIK Ruby's Math module doesn't provide acos/asin
|
|
# While we're at it, let's also define missing hyperbolic functions
|
|
module Math
|
|
def Math.asin(x)
|
|
atan2(x, sqrt(1 - x**2))
|
|
end
|
|
def Math.acos(x)
|
|
atan2(sqrt(1 - x**2), x)
|
|
end
|
|
def Math.atan(x)
|
|
atan2(x, 1)
|
|
end
|
|
def Math.sinh(x)
|
|
(exp(x) - exp(-x)) / 2
|
|
end
|
|
def Math.cosh(x)
|
|
(exp(x) + exp(-x)) / 2
|
|
end
|
|
def Math.tanh(x)
|
|
sinh(x) / cosh(x)
|
|
end
|
|
end
|
|
|
|
# The support for Complex numbers is not built-in
|
|
y = Math.acos(3.7)
|
|
#=> in `sqrt': square root for negative number (ArgumentError)
|
|
|
|
# There is an implementation of Complex numbers in 'complex.rb' in current
|
|
# Ruby distro, but it doesn't support atan2 with complex args, so it doesn't
|
|
# solve this problem.
|
|
|
|
|
|
# @@PLEAC@@_2.13
|
|
log_e = Math.log(val)
|
|
log_10 = Math.log10(val)
|
|
|
|
def log_base(base, val)
|
|
Math.log(val)/Math.log(base)
|
|
end
|
|
|
|
answer = log_base(10, 10_000)
|
|
puts "log10(10,000) = #{answer}"
|
|
|
|
|
|
# @@PLEAC@@_2.14
|
|
require 'matrix.rb'
|
|
|
|
a = Matrix[[3, 2, 3], [5, 9, 8]]
|
|
b = Matrix[[4, 7], [9, 3], [8, 1]]
|
|
c = a * b
|
|
|
|
a.row_size
|
|
a.column_size
|
|
|
|
c.det
|
|
a.transpose
|
|
|
|
|
|
# @@PLEAC@@_2.15
|
|
require 'complex.rb'
|
|
require 'rational.rb'
|
|
|
|
a = Complex(3, 5) # 3 + 5i
|
|
b = Complex(2, -2) # 2 - 2i
|
|
puts "c = #{a*b}"
|
|
|
|
c = a * b
|
|
d = 3 + 4*Complex::I
|
|
|
|
printf "sqrt(#{d}) = %s\n", Math.sqrt(d)
|
|
|
|
|
|
# @@PLEAC@@_2.16
|
|
number = hexadecimal.hex
|
|
number = octal.oct
|
|
|
|
print "Gimme a number in decimal, octal, or hex: "
|
|
num = gets.chomp
|
|
exit unless defined?(num)
|
|
num = num.oct if num =~ /^0/ # does both oct and hex
|
|
printf "%d %x %o\n", num, num, num
|
|
|
|
print "Enter file permission in octal: "
|
|
permissions = gets.chomp
|
|
raise "Exiting ...\n" unless defined?(permissions)
|
|
puts "The decimal value is #{permissions.oct}"
|
|
|
|
|
|
# @@PLEAC@@_2.17
|
|
def commify(n)
|
|
n.to_s =~ /([^\.]*)(\..*)?/
|
|
int, dec = $1.reverse, $2 ? $2 : ""
|
|
while int.gsub!(/(,|\.|^)(\d{3})(\d)/, '\1\2,\3')
|
|
end
|
|
int.reverse + dec
|
|
end
|
|
|
|
|
|
# @@PLEAC@@_2.18
|
|
printf "It took %d hour%s\n", time, time == 1 ? "" : "s"
|
|
|
|
# dunno if an equivalent to Lingua::EN::Inflect exists...
|
|
|
|
|
|
# @@PLEAC@@_2.19
|
|
#-----------------------------
|
|
#!/usr/bin/ruby
|
|
# bigfact - calculating prime factors
|
|
def factorize(orig)
|
|
factors = {}
|
|
factors.default = 0 # return 0 instead nil if key not found in hash
|
|
n = orig
|
|
i = 2
|
|
sqi = 4 # square of i
|
|
while sqi <= n do
|
|
while n.modulo(i) == 0 do
|
|
n /= i
|
|
factors[i] += 1
|
|
# puts "Found factor #{i}"
|
|
end
|
|
# we take advantage of the fact that (i +1)**2 = i**2 + 2*i +1
|
|
sqi += 2 * i + 1
|
|
i += 1
|
|
end
|
|
|
|
if (n != 1) && (n != orig)
|
|
factors[n] += 1
|
|
end
|
|
factors
|
|
end
|
|
|
|
def printfactorhash(orig, factorcount)
|
|
print format("%-10d ", orig)
|
|
if factorcount.length == 0
|
|
print "PRIME"
|
|
else
|
|
# sorts after number, because the hash keys are numbers
|
|
factorcount.sort.each { |factor,exponent|
|
|
print factor
|
|
if exponent > 1
|
|
print "**", exponent
|
|
end
|
|
print " "
|
|
}
|
|
end
|
|
puts
|
|
end
|
|
|
|
for arg in ARGV
|
|
n = arg.to_i
|
|
mfactors = factorize(n)
|
|
printfactorhash(n, mfactors)
|
|
end
|
|
#-----------------------------
|
|
|
|
|
|
# @@PLEAC@@_3.0
|
|
puts Time.now
|
|
|
|
print "Today is day ", Time.now.yday, " of the current year.\n"
|
|
print "Today is day ", Time.now.day, " of the current month.\n"
|
|
|
|
|
|
# @@PLEAC@@_3.1
|
|
day, month, year = Time.now.day, Time.now.month, Time.now.year
|
|
# or
|
|
day, month, year = Time.now.to_a[3..5]
|
|
|
|
tl = Time.now.localtime
|
|
printf("The current date is %04d %02d %02d\n", tl.year, tl.month, tl.day)
|
|
|
|
Time.now.localtime.strftime("%Y-%m-%d")
|
|
|
|
|
|
# @@PLEAC@@_3.2
|
|
Time.local(year, month, day, hour, minute, second).tv_sec
|
|
Time.gm(year, month, day, hour, minute, second).tv_sec
|
|
|
|
|
|
# @@PLEAC@@_3.3
|
|
sec, min, hour, day, month, year, wday, yday, isdst, zone = Time.at(epoch_secs).to_a
|
|
|
|
|
|
# @@PLEAC@@_3.4
|
|
when_ = now + difference # now -> Time ; difference -> Numeric (delta in seconds)
|
|
then_ = now - difference
|
|
|
|
|
|
# @@PLEAC@@_3.5
|
|
bree = 361535725
|
|
nat = 96201950
|
|
|
|
difference = bree - nat
|
|
puts "There were #{difference} seconds between Nat and Bree"
|
|
|
|
seconds = difference % 60
|
|
difference = (difference - seconds) / 60
|
|
minutes = difference % 60
|
|
difference = (difference - minutes) / 60
|
|
hours = difference % 24
|
|
difference = (difference - hours) / 24
|
|
days = difference % 7
|
|
weeks = (difference - days) / 7
|
|
|
|
puts "(#{weeks} weeks, #{days} days, #{hours}:#{minutes}:#{seconds})"
|
|
|
|
|
|
# @@PLEAC@@_3.6
|
|
monthday, weekday, yearday = date.mday, date.wday, date.yday
|
|
|
|
# AFAIK the week number is not just a division since week boundaries are on sundays
|
|
weeknum = d.strftime("%U").to_i + 1
|
|
|
|
year = 1981
|
|
month = "jun" # or `6' if you want to emulate a broken language
|
|
day = 16
|
|
t = Time.mktime(year, month, day)
|
|
print "#{month}/#{day}/#{year} was a ", t.strftime("%A"), "\n"
|
|
|
|
|
|
# @@PLEAC@@_3.7
|
|
yyyy, mm, dd = $1, $2, $3 if "1998-06-25" =~ /(\d+)-(\d+)-(\d+)/
|
|
|
|
epoch_seconds = Time.mktime(yyyy, mm, dd).tv_sec
|
|
|
|
# dunno an equivalent to Date::Manip#ParseDate
|
|
|
|
|
|
# @@PLEAC@@_3.8
|
|
string = Time.at(epoch_secs)
|
|
Time.at(1234567890).gmtime # gives: Fri Feb 13 23:31:30 UTC 2009
|
|
|
|
time = Time.mktime(1973, "jan", 18, 3, 45, 50)
|
|
print "In localtime it gives: ", time.localtime, "\n"
|
|
|
|
|
|
# @@PLEAC@@_3.9
|
|
# Ruby provides micro-seconds in Time object
|
|
Time.now.usec
|
|
|
|
# Ruby gives the seconds in floating format when substracting two Time objects
|
|
before = Time.now
|
|
line = gets
|
|
elapsed = Time.now - before
|
|
puts "You took #{elapsed} seconds."
|
|
|
|
# On my Celeron-400 with Linux-2.2.19-14mdk, average for three execs are:
|
|
# This Ruby version: average 0.00321 sec
|
|
# Cookbook's Perl version: average 0.00981 sec
|
|
size = 500
|
|
number_of_times = 100
|
|
total_time = 0
|
|
number_of_times.times {
|
|
# populate array
|
|
array = []
|
|
size.times { array << rand }
|
|
# sort it
|
|
begin_ = Time.now
|
|
array.sort!
|
|
time = Time.now - begin_
|
|
total_time += time
|
|
}
|
|
printf "On average, sorting %d random numbers takes %.5f seconds\n",
|
|
size, (total_time/Float(number_of_times))
|
|
|
|
|
|
# @@PLEAC@@_3.10
|
|
sleep(0.005) # Ruby is definitely not as broken as Perl :)
|
|
# (may be interrupted by sending the process a SIGALRM)
|
|
|
|
|
|
# @@PLEAC@@_3.11
|
|
#!/usr/bin/ruby -w
|
|
# hopdelta - feed mail header, produce lines
|
|
# showing delay at each hop.
|
|
require 'time'
|
|
class MailHopDelta
|
|
|
|
def initialize(mail)
|
|
@head = mail.gsub(/\n\s+/,' ')
|
|
@topline = %w-Sender Recipient Time Delta-
|
|
@start_from = mail.match(/^From.*\@([^\s>]*)/)[1]
|
|
@date = Time.parse(mail.match(/^Date:\s+(.*)/)[1])
|
|
end
|
|
|
|
def out(line)
|
|
"%-20.20s %-20.20s %-20.20s %s" % line
|
|
end
|
|
|
|
def hop_date(day)
|
|
day.strftime("%I:%M:%S %Y/%m/%d")
|
|
end
|
|
|
|
def puts_hops
|
|
puts out(@topline)
|
|
puts out(['Start', @start_from, hop_date(@date),''])
|
|
@head.split(/\n/).reverse.grep(/^Received:/).each do |hop|
|
|
hop.gsub!(/\bon (.*?) (id.*)/,'; \1')
|
|
whence = hop.match(/;\s+(.*)$/)[1]
|
|
unless whence
|
|
warn "Bad received line: #{hop}"
|
|
next
|
|
end
|
|
from = $+ if hop =~ /from\s+(\S+)|\((.*?)\)/
|
|
by = $1 if hop =~ /by\s+(\S+\.\S+)/
|
|
next unless now = Time.parse(whence).localtime
|
|
delta = now - @date
|
|
puts out([from, by, hop_date(now), hop_time(delta)])
|
|
@date = now
|
|
end
|
|
end
|
|
|
|
def hop_time(secs)
|
|
sign = secs < 0 ? -1 : 1
|
|
days, secs = secs.abs.divmod(60 * 60 * 24)
|
|
hours,secs = secs.abs.divmod(60 * 60)
|
|
mins, secs = secs.abs.divmod(60)
|
|
rtn = "%3ds" % [secs * sign]
|
|
rtn << "%3dm" % [mins * sign] if mins != 0
|
|
rtn << "%3dh" % [hours * sign] if hours != 0
|
|
rtn << "%3dd" % [days * sign] if days != 0
|
|
rtn
|
|
end
|
|
end
|
|
|
|
$/ = ""
|
|
mail = MailHopDelta.new(ARGF.gets).puts_hops
|
|
|
|
|
|
# @@PLEAC@@_4.0
|
|
single_level = [ "this", "that", "the", "other" ]
|
|
|
|
# Ruby directly supports nested arrays
|
|
double_level = [ "this", "that", [ "the", "other" ] ]
|
|
still_single_level = [ "this", "that", [ "the", "other" ] ].flatten
|
|
|
|
|
|
# @@PLEAC@@_4.1
|
|
a = [ "quick", "brown", "fox" ]
|
|
a = %w(Why are you teasing me?)
|
|
|
|
lines = <<"END_OF_HERE_DOC".gsub(/^\s*(.+)/, '\1')
|
|
The boy stood on the burning deck,
|
|
It was as hot as glass.
|
|
END_OF_HERE_DOC
|
|
|
|
bigarray = IO.readlines("mydatafile").collect { |l| l.chomp }
|
|
|
|
name = "Gandalf"
|
|
banner = %Q(Speak, #{name}, and welcome!)
|
|
|
|
host_info = `host #{his_host}`
|
|
|
|
%x(ps #{$$})
|
|
|
|
banner = 'Costs only $4.95'.split(' ')
|
|
|
|
rax = %w! ( ) < > { } [ ] !
|
|
|
|
|
|
# @@PLEAC@@_4.2
|
|
def commify_series(arr)
|
|
return '' if not arr
|
|
case arr.size
|
|
when 0 then ''
|
|
when 1 then arr[0]
|
|
when 2 then arr.join(' and ')
|
|
else arr[0..-2].join(', ') + ', and ' + arr[-1]
|
|
end
|
|
end
|
|
|
|
array = [ "red", "yellow", "green" ]
|
|
|
|
print "I have ", array, " marbles\n"
|
|
# -> I have redyellowgreen marbles
|
|
|
|
# But unlike Perl:
|
|
print "I have #{array} marbles\n"
|
|
# -> I have redyellowgreen marbles
|
|
# So, needs:
|
|
print "I have #{array.join(' ')} marbles\n"
|
|
# -> I have red yellow green marbles
|
|
|
|
#!/usr/bin/ruby
|
|
# communify_series - show proper comma insertion in list output
|
|
|
|
def commify_series(arr)
|
|
return '' if not arr
|
|
sepchar = arr.find { |p| p =~ /,/ } ? '; ' : ', '
|
|
case arr.size
|
|
when 0 then ''
|
|
when 1 then arr[0]
|
|
when 2 then arr.join(' and ')
|
|
else arr[0..-2].join(sepchar) + sepchar + 'and ' + arr[-1]
|
|
end
|
|
end
|
|
|
|
lists = [
|
|
[ 'just one thing' ],
|
|
%w(Mutt Jeff),
|
|
%w(Peter Paul Mary),
|
|
[ 'To our parents', 'Mother Theresa', 'God' ],
|
|
[ 'pastrami', 'ham and cheese', 'peanut butter and jelly', 'tuna' ],
|
|
[ 'recycle tired, old phrases', 'ponder big, happy thoughts' ],
|
|
[ 'recycle tired, old phrases',
|
|
'ponder big, happy thoughts',
|
|
'sleep and dream peacefully' ],
|
|
]
|
|
|
|
for list in lists do
|
|
puts "The list is: #{commify_series(list)}."
|
|
end
|
|
|
|
|
|
# @@PLEAC@@_4.3
|
|
# (note: AFAIK Ruby doesn't allow gory change of Array length)
|
|
# grow the array by assigning nil to past the end of array
|
|
ary[new_size-1] = nil
|
|
# shrink the array by slicing it down
|
|
ary.slice!(new_size..-1)
|
|
# init the array with given size
|
|
Array.new(number_of_elems)
|
|
# assign to an element past the original end enlarges the array
|
|
ary[index_new_last_elem] = value
|
|
|
|
def what_about_that_array(a)
|
|
print "The array now has ", a.size, " elements.\n"
|
|
# Index of last element is not really interesting in Ruby
|
|
print "Element #3 is `#{a[3]}'.\n"
|
|
end
|
|
people = %w(Crosby Stills Nash Young)
|
|
what_about_that_array(people)
|
|
|
|
|
|
# @@PLEAC@@_4.4
|
|
# OO style
|
|
bad_users.each { |user|
|
|
complain(user)
|
|
}
|
|
# or, functional style
|
|
for user in bad_users
|
|
complain(user)
|
|
end
|
|
|
|
for var in ENV.keys.sort
|
|
puts "#{var}=#{ENV[var]}"
|
|
end
|
|
|
|
for user in all_users
|
|
disk_space = get_usage(user)
|
|
if (disk_space > MAX_QUOTA)
|
|
complain(user)
|
|
end
|
|
end
|
|
|
|
for l in IO.popen("who").readlines
|
|
print l if l =~ /^gc/
|
|
end
|
|
|
|
# we can mimic the obfuscated Perl way
|
|
while fh.gets # $_ is set to the line just read
|
|
chomp # $_ has a trailing \n removed, if it had one
|
|
split.each { |w| # $_ is split on whitespace
|
|
# but $_ is not set to each chunk as in Perl
|
|
print w.reverse
|
|
}
|
|
end
|
|
# ...or use a cleaner way
|
|
for l in fh.readlines
|
|
l.chomp.split.each { |w| print w.reverse }
|
|
end
|
|
|
|
# same drawback as in problem 1.4, we can't mutate a Numeric...
|
|
array.collect! { |v| v - 1 }
|
|
|
|
a = [ .5, 3 ]; b = [ 0, 1 ]
|
|
for ary in [ a, b ]
|
|
ary.collect! { |v| v * 7 }
|
|
end
|
|
puts "#{a.join(' ')} #{b.join(' ')}"
|
|
|
|
# we can mutate Strings, cool; we need a trick for the scalar
|
|
for ary in [ [ scalar ], array, hash.values ]
|
|
ary.each { |v| v.strip! } # String#strip rules :)
|
|
end
|
|
|
|
|
|
# @@PLEAC@@_4.5
|
|
# not relevant in Ruby since we have always references
|
|
for item in array
|
|
# do somethingh with item
|
|
end
|
|
|
|
|
|
# @@PLEAC@@_4.6
|
|
unique = list.uniq
|
|
|
|
# generate a list of users logged in, removing duplicates
|
|
users = `who`.collect { |l| l =~ /(\w+)/; $1 }.sort.uniq
|
|
puts("users logged in: #{commify_series(users)}") # see 4.2 for commify_series
|
|
|
|
|
|
# @@PLEAC@@_4.7
|
|
a - b
|
|
# [ 1, 1, 2, 2, 3, 3, 3, 4, 5 ] - [ 1, 2, 4 ] -> [3, 5]
|
|
|
|
|
|
# @@PLEAC@@_4.8
|
|
union = a | b
|
|
intersection = a & b
|
|
difference = a - b
|
|
|
|
|
|
# @@PLEAC@@_4.9
|
|
array1.concat(array2)
|
|
# if you will assign to another object, better use:
|
|
new_ary = array1 + array2
|
|
|
|
members = [ "Time", "Flies" ]
|
|
initiates = [ "An", "Arrow" ]
|
|
members += initiates
|
|
|
|
members = [ "Time", "Flies" ]
|
|
initiates = [ "An", "Arrow" ]
|
|
members[2,0] = [ "Like", initiates ].flatten
|
|
|
|
members[0] = "Fruit"
|
|
members[3,2] = "A", "Banana"
|
|
|
|
|
|
# @@PLEAC@@_4.10
|
|
reversed = ary.reverse
|
|
|
|
ary.reverse_each { |e|
|
|
# do something with e
|
|
}
|
|
|
|
descending = ary.sort.reverse
|
|
descending = ary.sort { |a,b| b <=> a }
|
|
|
|
|
|
# @@PLEAC@@_4.11
|
|
# remove n elements from front of ary (shift n)
|
|
front = ary.slice!(0, n)
|
|
|
|
# remove n elements from the end of ary (pop n)
|
|
end_ = ary.slice!(-n .. -1)
|
|
|
|
# let's extend the Array class, to make that useful
|
|
class Array
|
|
def shift2()
|
|
slice!(0 .. 1) # more symetric with pop2...
|
|
end
|
|
def pop2()
|
|
slice!(-2 .. -1)
|
|
end
|
|
end
|
|
|
|
friends = %w(Peter Paul Mary Jim Tim)
|
|
this, that = friends.shift2
|
|
|
|
beverages = %w(Dew Jolt Cola Sprite Fresca)
|
|
pair = beverages.pop2
|
|
|
|
|
|
# @@PLEAC@@_4.12
|
|
# use Enumerable#detect (or the synonym Enumerable#find)
|
|
highest_eng = employees.detect { |emp| emp.category == 'engineer' }
|
|
|
|
|
|
# @@PLEAC@@_4.13
|
|
# use Enumerable#select (or the synonym Enumerable#find_all)
|
|
bigs = nums.select { |i| i > 1_000_000 }
|
|
pigs = users.keys.select { |k| users[k] > 1e7 }
|
|
|
|
matching = `who`.select { |u| u =~ /^gnat / }
|
|
|
|
engineers = employees.select { |e| e.position == 'Engineer' }
|
|
|
|
secondary_assistance = applicants.select { |a|
|
|
a.income >= 26_000 && a.income < 30_000
|
|
}
|
|
|
|
|
|
# @@PLEAC@@_4.14
|
|
# normally you would have an array of Numeric (Float or
|
|
# Fixnum or Bignum), so you would use:
|
|
sorted = unsorted.sort
|
|
# if you have strings representing Integers or Floats
|
|
# you may specify another sort method:
|
|
sorted = unsorted.sort { |a,b| a.to_f <=> b.to_f }
|
|
|
|
# let's use the list of my own PID's
|
|
`ps ux`.split("\n")[1..-1].
|
|
select { |i| i =~ /^#{ENV['USER']}/ }.
|
|
collect { |i| i.split[1] }.
|
|
sort { |a,b| a.to_i <=> b.to_i }.each { |i| puts i }
|
|
puts "Select a process ID to kill:"
|
|
pid = gets.chomp
|
|
raise "Exiting ... \n" unless pid && pid =~ /^\d+$/
|
|
Process.kill('TERM', pid.to_i)
|
|
sleep 2
|
|
Process.kill('KILL', pid.to_i)
|
|
|
|
descending = unsorted.sort { |a,b| b.to_f <=> a.to_f }
|
|
|
|
|
|
# @@PLEAC@@_4.15
|
|
ordered = unordered.sort { |a,b| compare(a,b) }
|
|
|
|
precomputed = unordered.collect { |e| [compute, e] }
|
|
ordered_precomputed = precomputed.sort { |a,b| a[0] <=> b[0] }
|
|
ordered = ordered_precomputed.collect { |e| e[1] }
|
|
|
|
ordered = unordered.collect { |e| [compute, e] }.
|
|
sort { |a,b| a[0] <=> b[0] }.
|
|
collect { |e| e[1] }
|
|
|
|
for employee in employees.sort { |a,b| a.name <=> b.name }
|
|
print employee.name, " earns \$ ", employee.salary, "\n"
|
|
end
|
|
|
|
# Beware! `0' is true in Ruby.
|
|
# For chaining comparisons, you may use Numeric#nonzero?, which
|
|
# returns num if num is not zero, nil otherwise
|
|
sorted = employees.sort { |a,b| (a.name <=> b.name).nonzero? || b.age <=> a.age }
|
|
|
|
users = []
|
|
# getpwent is not wrapped in Ruby... let's fallback
|
|
IO.readlines('/etc/passwd').each { |u| users << u.split(':') }
|
|
users.sort! { |a,b| a[0] <=> b[0] }
|
|
for user in users
|
|
puts user[0]
|
|
end
|
|
|
|
sorted = names.sort { |a,b| a[1, 1] <=> b[1, 1] }
|
|
sorted = strings.sort { |a,b| a.length <=> b.length }
|
|
|
|
# let's show only the compact version
|
|
ordered = strings.collect { |e| [e.length, e] }.
|
|
sort { |a,b| a[0] <=> b[0] }.
|
|
collect { |e| e[1] }
|
|
|
|
ordered = strings.collect { |e| [/\d+/.match(e)[0].to_i, e] }.
|
|
sort { |a,b| a[0] <=> b[0] }.
|
|
collect { |e| e[1] }
|
|
|
|
print `cat /etc/passwd`.collect { |e| [e, e.split(':').indexes(3,2,0)].flatten }.
|
|
sort { |a,b| (a[1] <=> b[1]).nonzero? || (a[2] <=> b[2]).nonzero? || a[3] <=> b[3] }.
|
|
collect { |e| e[0] }
|
|
|
|
|
|
# @@PLEAC@@_4.16
|
|
circular.unshift(circular.pop) # the last shall be first
|
|
circular.push(circular.shift) # and vice versa
|
|
|
|
def grab_and_rotate(l)
|
|
l.push(ret = l.shift)
|
|
ret
|
|
end
|
|
|
|
processes = [1, 2, 3, 4, 5]
|
|
while (1)
|
|
process = grab_and_rotate(processes)
|
|
puts "Handling process #{process}"
|
|
sleep 1
|
|
end
|
|
|
|
|
|
# @@PLEAC@@_4.17
|
|
def fisher_yates_shuffle(a)
|
|
(a.size-1).downto(1) { |i|
|
|
j = rand(i+1)
|
|
a[i], a[j] = a[j], a[i] if i != j
|
|
}
|
|
end
|
|
|
|
def naive_shuffle(a)
|
|
for i in 0...a.size
|
|
j = rand(a.size)
|
|
a[i], a[j] = a[j], a[i]
|
|
end
|
|
end
|
|
|
|
|