Related
I'm pretty new at Ada, and I have a question. This Demo_Array_Sort from Rosetta Code uses the function "<" to determine how to sort the array. It sorts it by the name in alphabetical order. I understand this part. Where my question comes in is this:
If the array Data had multiple entries with the same name and I wanted to sort the list by value within Name, how would I do that? I've tried messing around with redefining the "<" function to no avail. Please help!
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Containers.Generic_Array_Sort;
procedure Demo_Array_Sort is
function "+" (S : String) return Unbounded_String renames To_Unbounded_String;
type A_Composite is
record
Name : Unbounded_String;
Value : Unbounded_String;
end record;
function "<" (L, R : A_Composite) return Boolean is
begin
return L.Name < R.Name;
end "<";
procedure Put_Line (C : A_Composite) is
begin
Put_Line (To_String (C.Name) & " " & To_String (C.Value));
end Put_Line;
type An_Array is array (Natural range <>) of A_Composite;
procedure Sort is new Ada.Containers.Generic_Array_Sort (Natural, A_Composite, An_Array);
Data : An_Array := (1 => (Name => +"Joe", Value => +"5531"),
2 => (Name => +"Adam", Value => +"2341"),
3 => (Name => +"Bernie", Value => +"122"),
4 => (Name => +"Walter", Value => +"1234"),
5 => (Name => +"David", Value => +"19"));
begin
Sort (Data);
for I in Data'Range loop
Put_Line (Data (I));
end loop;
end Demo_Array_Sort;
Example Data:
Data : An_Array := (1 => (Name => +"Joe", Value => +"5531"),
2 => (Name => +"Adam", Value => +"2341"),
3 => (Name => +"Bernie", Value => +"122"),
4 => (Name => +"Walter", Value => +"1234"),
5 => (Name => +"David", Value => +"19")
6 => (Name => +"David", Value => +"42")
7 => (Name => +"David", Value => +"5"));
Would output:
Adam 2341
Bernie 122
David 5
David 19
David 42
Joe 5531
Walter 1234
In outline,
Change the Value component of A_Composite to a scalar subtype for which "<" is already suitably defined; I've chosen Natural:
type A_Composite is
record
Name : Unbounded_String;
Value : Natural;
end record;
Now it's easy to write a "<" that handles L.Name = R.Name:
function "<" (L, R : A_Composite) return Boolean is
begin
if L.Name < R.Name then return True;
elsif L.Name = R.Name then return L.Value < R.Value;
else return False;
end if;
end "<";
Update Put_Line accordingly:
Put_Line (To_String (C.Name) & Natural'Image(C.Value));
Data:
Data : An_Array := (
1 => (Name => +"Joe", Value => 5531 ),
2 => (Name => +"Adam", Value => 2341),
3 => (Name => +"Bernie", Value => 122),
4 => (Name => +"Walter", Value => 1234),
5 => (Name => +"David", Value => 19),
6 => (Name => +"David", Value => 42),
7 => (Name => +"David", Value => 5));
Console:
Adam 2341
Bernie 122
David 5
David 19
David 42
Joe 5531
Walter 1234
Following is an example using the Vector container.
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Containers.Vectors;
procedure Main is
type Surname is (Smith, Jones, Chen, Chavez);
type Name is (John, Francis, Leslie, Margaret, George, Walter);
type Person is record
First : Name;
Last : Surname;
end record;
function Less(P1: Person; P2 : Person) return boolean is
begin
if P1.Last < P2.Last then
return true;
elsif
P1.Last = P2.Last then
return P1.First < P2.First;
else
return false;
end if;
end Less;
procedure Print(P : Person) is
begin
Put_Line(P.Last'Image & ", " & P.First'Image);
end Print;
package Person_Vector is new Ada.Containers.Vectors(Index_Type => Natural,
Element_Type => Person);
use Person_Vector;
package Person_Sort is new Generic_Sorting(Less);
use Person_Sort;
V : Vector;
Temp : Person;
begin
for N in Name loop
for S in Surname loop
Temp.First := N;
Temp.Last := S;
V.Append(Temp);
end loop;
end loop;
Put_Line("Unsorted list:");
for P of V loop
Print(P);
end loop;
New_Line;
Sort(V);
Put_Line("Sorted list:");
for P of V loop
Print(P);
end loop;
end Main;
The output of this program is:
Unsorted list:
SMITH, JOHN
JONES, JOHN
CHEN, JOHN
CHAVEZ, JOHN
SMITH, FRANCIS
JONES, FRANCIS
CHEN, FRANCIS
CHAVEZ, FRANCIS
SMITH, LESLIE
JONES, LESLIE
CHEN, LESLIE
CHAVEZ, LESLIE
SMITH, MARGARET
JONES, MARGARET
CHEN, MARGARET
CHAVEZ, MARGARET
SMITH, GEORGE
JONES, GEORGE
CHEN, GEORGE
CHAVEZ, GEORGE
SMITH, WALTER
JONES, WALTER
CHEN, WALTER
CHAVEZ, WALTER
Sorted list:
SMITH, JOHN
SMITH, FRANCIS
SMITH, LESLIE
SMITH, MARGARET
SMITH, GEORGE
SMITH, WALTER
JONES, JOHN
JONES, FRANCIS
JONES, LESLIE
JONES, MARGARET
JONES, GEORGE
JONES, WALTER
CHEN, JOHN
CHEN, FRANCIS
CHEN, LESLIE
CHEN, MARGARET
CHEN, GEORGE
CHEN, WALTER
CHAVEZ, JOHN
CHAVEZ, FRANCIS
CHAVEZ, LESLIE
CHAVEZ, MARGARET
CHAVEZ, GEORGE
CHAVEZ, WALTER
Note that the fields are sorted in the order their enumerations are specified in the program.
I have a hash here:
VALID_CHOICES = {
'r' => 'rock',
'p' => 'paper',
'sc' => 'scissors',
'l' => 'lizard',
'sp' => 'spock'
}
And a method which basically compares here:
def win?(first, second)
(first == 'sc' && second == 'p') ||
(first == 'p' && second == 'r') ||
(first == 'r' && second == 'l') ||
(first == 'l' && second == 'sp') ||
(first == 'sp' && second == 'sc') ||
(first == 'sc' && second == 'l') ||
(first == 'l' && second == 'p') ||
(first == 'p' && second == 'sp') ||
(first == 'sp' && second == 'r') ||
(first == 'r' && second == 'sc')
end
How can I rewrite my method in very short concise code that means exactly the same thing? Any idea? Is it possible to do it using hashes?
You should define clear rules for what each token can win:
WINS = {
'r' => %w{l sc},
'p' => %w{r sp},
'sc' => %w{p l},
'l' => %w{p sp},
'sp' => %w{r sc}
}
Now you can determine wins using a simple lookup:
def win?(first, second)
WINS[first].include?(second)
end
While there may be several 'clever' ways to avoid an explicit structure like WINS, explicit rules are much more understandable - and therefore, more maintainable. Conciseness in code is considered a positive attribute where it improves the readability of the code. Conciseness to the extreme that causes the code to be difficult to understand is not something to strive for.
In addition to user2864740's comment and Cary Swoveland's explanation, you could also use a hash to map "winning pairs" to their respective verb:
WINS = {
%w[scissors paper] => 'cuts',
%w[paper rock] => 'covers',
%w[rock lizard] => 'crushes',
%w[lizard spock] => 'poisons',
%w[spock scissors] => 'smashes',
%w[scissors lizard] => 'decapitates',
%w[lizard paper] => 'eats',
%w[paper spock] => 'disproves',
%w[spock rock] => 'vaporizes',
%w[rock scissors] => 'crushes'
}
It returns the corresponding verb if the key's first item beats the second:
WINS[['paper', 'rock']] #=> "covers"
and nil if it doesn't:
WINS[['rock', 'paper']] #=> nil
In your method:
def win?(first, second)
WINS.has_key?([first, second])
end
Or to check both sides:
if WINS.has_key?([first, second])
# first wins
elsif WINS.has_key?([second, first])
# second wins
else
# tie
end
Or more verbose:
def result(first, second)
if verb = WINS[[first, second]]
"first wins: #{first} #{verb} #{second}"
elsif verb = WINS[[second, first]]
"second wins: #{second} #{verb} #{first}"
else
"tie"
end
end
result('rock', 'scissors')
#=> "first wins: rock crushes scissors"
result('spock', 'lizard')
#=> "second wins: lizard poisons spock"
result('paper', 'paper')
#=> "tie"
Of course, you can also use your abbreviations (sc, p, r, l, sp) instead of whole words.
irb(main):009:0> a = "good"
=> "good"
irb(main):010:0> a = "good" + "morning"
=> "goodmorning"
irb(main):011:0> a = "good"
=> "good"
irb(main):012:0> a << " morning"
=> "good morning"
Till now both the concatenation operators work fine.
irb(main):013:0> a = "good"
=> "good"
irb(main):014:0> a.freeze
=> "good"
irb(main):015:0> a.frozen?
=> true
irb(main):016:0> a << " welcome"
RuntimeError: can't modify frozen String
from (irb):16
from /usr/bin/irb:12:in `<main>'
irb(main):017:0> a = a + " welcome"
=> "good welcome"
But with a frozen string a difference is clearly visible from IRB that << and + are not behaving as they are supposed to. Could anyone tell me the reason for this?
They are doing exactly what they're supposed to. << modifies the string it is called on, while + is closer to str.dup << arg. This behavior is the defined, documented standard.
#Linuxios answer is perfect.
But still here I have tried to show that modification with more transparent way:
#ubuntu:~$ irb --simple-prompt
>> a = "good"
=> "good"
>> a.freeze
=> "good"
>> a.frozen?
=> true
>> a.object_id
=> 10557720 # holds the reference to the "good" string object.
>> a = a + " morning"
=> "good morning"
>> a.object_id
=> 10415700 # holds the reference to the new string object "good morning".
>> a.frozen?
=> false
>> ObjectSpace._id2ref(10415700)
=> "good morning"
>> ObjectSpace._id2ref(10557720)
=> "good"
>> ObjectSpace._id2ref(10557720).frozen?
=> true
We can conclude that - yes , string "good" is still frozen. Only the thing what happened is a referencing the new object "good morning". Only the reference assignment to a has been changed.
<< and + behave differently on ALL string objects, not just frozen ones. One (<<) modifies a string, and the other (+) returns a new string without modifying the original:
With <<:
string1 = "good"
#=> "good"
string2 = string1
#=> "good"
string1 << " morning"
#=> "good morning"
string1
#=> "good morning"
string2
#=> "good morning"
With +:
string1 = "good"
#=> "good"
string2 = string1
#=> "good"
string1 = string1 + " morning"
#=> "good morning"
string1
#=> "good morning"
string2
#=> "good"
Object#freeze is specifically designed to disallow the sort of behavior I demonstrated above with << (it prevents objects from being modified), so calling << on a frozen string results in an error.
Sequel supports a boolean type.
It stores true and false as t and f in SQLite.
If you read again, the data are converted back to true and false
SQLite itself prefers to store true as 1 and false as 0 (and some SQLite-administartion tools expect it).
If I store my boolean also as '1', Sequel also converts the value to 'true':
require 'sequel'
DB = Sequel.sqlite()#'test.db')
DB.create_table(:test){
boolean :my_truth
nvarchar :descr, :size => 10
}
DB[:test].insert(1, '1')
p DB[:test].filter(:descr => '1').first #-> {:my_truth=>true, :descr=>"1"}
But if I select for true, no value is found:
DB[:test].filter(:my_truth => true).each{|ds|
puts "\t%s" % ds[:descr]
}
The values true and "t" are found:
DB[:test].insert(true, 'true')
DB[:test].insert('t', '"t"')
DB[:test].filter(:my_truth => true).each{|ds|
puts "\t%s" % ds[:descr] #true and 't'
}
There is a similar situation for false-values like 0... (see example code after the question)
So my question:
How can i make DB[:test].filter(:my_truth => true) to detect 1-values and
DB[:test].filter(:my_truth => false) to detect 0-values?
I'm not looking for something like DB[:test].filter(:my_truth => [true,1])
Similar question for Activerecord
Rails 3 SQLite3 Boolean false
I use Sequel 3.33.0
Example code:
require 'sequel'
DB = Sequel.sqlite()#'test.db')
DB.create_table(:test){
boolean :my_truth
nvarchar :descr, :size => 10
fixnum :line #code line, where data is inserted
}
#All true:
DB[:test].insert(true, 'true', __LINE__)
DB[:test].insert('true', '"true"', __LINE__)
DB[:test].insert(1, 'one', __LINE__)
DB[:test].insert('t', 't', __LINE__)
#All false:
DB[:test].insert(false,'false', __LINE__)
DB[:test].insert('false','"false"', __LINE__)
DB[:test].insert(0,'zero', __LINE__)
DB[:test].insert('f', 'f', __LINE__)
DB[:test].insert('F', 'F', __LINE__)
DB[:test].insert(nil, 'nil', __LINE__)
DB[:test].insert('n', 'n', __LINE__)
DB[:test].insert('N', 'N', __LINE__)
#Also true
DB[:test].insert('x', 'x', __LINE__)
DB[:test].insert(' ', 'space', __LINE__)
DB[:test].insert('', 'empty', __LINE__)
puts "All true values:"
DB[:test].filter(:my_truth => true).each{|ds|
puts "\t%s (line %i)" % [ds[:descr], ds[:line] ]
}
puts "All false values:"
DB[:test].filter(:my_truth => false).each{|ds|
puts "\t%s (line %i)" % [ds[:descr], ds[:line] ]
}
puts "Data:"
DB[:test].each{|ds|
puts "\t%-5s is <%s> (line %i)" % [ ds[:descr], ds[:my_truth].inspect, ds[:line] ]
}
Result:
All true values:
true (line 10)
t (line 13)
All false values:
false (line 16)
f (line 19)
Data:
true is <true> (line 10)
"true" is <true> (line 11)
one is <true> (line 12)
t is <true> (line 13)
false is <false> (line 16)
"false" is <false> (line 17)
zero is <false> (line 18)
f is <false> (line 19)
F is <false> (line 20)
nil is <nil> (line 21)
n is <false> (line 22)
N is <false> (line 23)
x is <true> (line 26)
space is <true> (line 27)
empty is <true> (line 28)
You can use the integer_booleans setting to use 1/0 as true/false, instead of 't'/'f', see http://sequel.rubyforge.org/rdoc-adapters/classes/Sequel/SQLite/DatabaseMethods.html. Here's an example:
DB = Sequel.sqlite(:integer_booleans=>true)
I'm in an introductory software development class, and my homework is to
create a rock paper scissors program that takes two arguments (rock,
paper), etc, and returns the arg that wins.
Now I would make quick work of this problem if I could use conditionals,
but the assignment says everything we need to know is in the first three
chapters of the ruby textbook, and these chapters DO NOT include
conditionals! Would it be possible to create this program without them?
Or is he just expecting us to be resourceful and use the conditionals?
It's a very easy assignment with conditionals though...I'm thinking that
I might be missing something here.
EDIT: I'm thinking of that chmod numerical system and think a solution may be possible through that additive system...
Here's one only using hashes:
RULES = {
:rock => {:rock => :draw, :paper => :paper, :scissors => :rock},
:paper => {:rock => :paper, :paper => :draw, :scissors => :scissors},
:scissors => {:rock => :rock, :paper => :scissors, :scissors => :draw}
}
def play(p1, p2)
RULES[p1][p2]
end
puts play(:rock, :paper) # :paper
puts play(:scissors, :rock) # :rock
puts play(:scissors, :scissors) # :draw
def winner(p1, p2)
wins = {rock: :scissors, scissors: :paper, paper: :rock}
{true => p1, false => p2}[wins[p1] == p2]
end
winner(:rock, :rock) # => :rock d'oh! – tokland
Per #sarnold, leaving this as an exercise for the student :).
I very much doubt you've seen array/set intersections, so just for fun:
def who_wins(p1, p2)
win_moves = {"rock" => "paper", "paper" => "scissors", "scissors" => "rock"}
([p1, p2] & win_moves.values_at(p1, p2)).first
end
who_wins("rock", "paper") # "paper"
who_wins("scissors", "rock") # "rock"
who_wins("scissors", "scissors") # nil
A simple hash to the rescue:
def tell_me(a1, a2)
input = [a1 , a2].sort.join('_').to_sym
rules = { :paper_rock => "paper", :rock_scissor => "rock", :paper_scissor => "scissor"}
rules[input]
end
I just think the simplest solution has to be something like:
#results = {
'rock/paper' => 'paper',
'rock/scissors' => 'rock',
'paper/scissors' => 'scissors',
'paper/rock' => 'paper',
'scissors/paper' => 'scissors',
'scissors/rock' => 'rock'
}
def winner p1, p2
#results["#{p1}/#{p2}"]
end
WINNAHS = [[:rock, :scissors], [:scissors, :paper], [:paper, :rock]]
def winner(p1, p2)
(WINNAHS.include?([p1,p2]) && p1) || (WINNAHS.include?([p2,p1]) && p2) || :tie
end
winner(:rock, :paper) #=> :paper
winner(:scissors, :paper) #=> :scissors
winner(:scissors, :scissors) #=> :tie
I don't know much about ruby, but I solved a problem like this long ago by using values for each one (eg, R = 1, P = 2, S=3).
Actually, I just googled after thinking about that and someone solved the problem in python using an array.
pguardiario's solution above can be modified per the below to show both (1) which player won (as opposed to the choice of object that won) and (2) the result when there is a draw:
def rps(p1, p2)
#results = {
'rock/paper' => "Player 2 won!",
'rock/scissors' => "Player 1 won!",
'paper/scissors' => "Player 2 won!",
'paper/rock' => "Player 1 won!",
'scissors/paper' => "Player 1 won!",
'scissors/rock' => "Player 2 won!",
'rock/rock' => "Draw!",
'scissors/scissors' => "Draw!",
'paper/paper' => "Draw!"
}
#results["#{p1}/#{p2}"]
end
rps("rock", "rock") => "Draw!"
rps("rock", "scissors") => "Player 1 won!"
rps("rock", "paper") => "Player 2 won!"
...etc