I know this seems to be a stupid question but I cannot figure out what
def Nodes = Node.findAllByParent(theNode).sort{ a, b -> a.label <=> b.label }
does? The Node class contains label and other attributes. I want to know what the sort thing in the above line does. theNode is like a parent node which has children. and how is it different from
def Nodes = Node.findAllByParent(theNode,sort['label'])
a <=> b
is shorthand for
a.compareTo(b)
which itself is equivalent to:
if (a > b) {
return 1
} else if (a < b) {
return -1
} else {
// a and b are equal
return 0
}
The difference between
def Nodes = Node.findAllByParent(theNode).sort{ a, b -> a.label <=> b.label }
and
def Nodes = Node.findAllByParent(theNode,sort['label'])
is that the first one does the sorting in-memory, whereas in the second case the nodes are returned in sorted order by the query. In general you should let the database do the sorting where possible.
By the way, I think the second parameter above should be [sort: "label"] rather than sort['label'].
The first sort is done as a Groovy sort on the collection where as the second is using the sorting capabilities of the data source (e.g. database ORDER BY).
The <=> is known as the spaceship operator. The operator is another way of referring to the compareTo method of the Comparable interface. This means we can implement the compareTo method in our own classes and this will allow us to use the <=> operator in our code. And of course all classes which already have implemented the compareTo method can be used with the spaceship operator. The operator makes for good readable sort methods.
For example:
class Person implements Comparable {
String username
String email
int compareTo(other) {
this.username <=> other.username
}
}
assert -1 == ('a' <=> 'b')
assert 0 == (42 <=> 42)
assert -1 == (new Person([username:'foo', email: 'test#email.com']) <=> new Person([username:'zebra', email:'tester#email.com']))
assert [1, 2, 3, 4] == [4, 2, 1, 3].sort{ a, b -> a <=> b }
Related
I saw this example in "Programming in Scala" chapter 24 "Collections in depth". This example shows two alternative ways to implement a tree:
by extending Traversable[Int] - here the complexity of def foreach[U](f: Int => U): Unit would be O(N).
by extending Iterable[Int] - here the complexity of def iterator: Iterator[Int] would be O(N log(N)).
This is to demonstrate why it would be helpful to have two separate traits, Traversable and Iterable.
sealed abstract class Tree
case class Branch(left: Tree, right: Tree) extends Tree
case class Node(elem: Int) extends Tree
sealed abstract class Tree extends Traversable[Int] {
def foreach[U](f: Int => U) = this match {
case Node(elem) => f(elem)
case Branch(l, r) => l foreach f; r foreach f
}
}
sealed abstract class Tree extends Iterable[Int] {
def iterator: Iterator[Int] = this match {
case Node(elem) => Iterator.single(elem)
case Branch(l, r) => l.iterator ++ r.iterator
}
}
Regarding the implementation of foreach they say:
traversing a balanced tree takes time proportional to the number of
elements in the tree. To see this, consider that for a balanced tree
with N leaves you will have N - 1 interior nodes of class Branch. So
the total number of steps to traverse the tree is N + N - 1.
That makes sense. :)
However, they mention that the concatenation of the two iterators in the iterator method has time complexity of log(N), so the total complexity of the method would be N log(N):
Every time an element is produced by a concatenated iterator such as
l.iterator ++ r.iterator, the computation needs to follow one
indirection to get at the right iterator (either l.iterator, or
r.iterator). Overall, that makes log(N) indirections to get at a leaf
of a balanced tree with N leaves. So the cost of visiting all elements of a tree went up from about 2N for the foreach traversal method to N log(N) for the traversal with iterator.
????
Why does the computation of the concatenated iterator need to get at a leaf of the left or right iterator?
The pun on "collections in depth" is apt. The depth of the data structure matters.
When you invoke top.iterator.next(), each interior Branch delegates to the iterator of the Branch or Node below it, a call chain which is log(N).
You incur that call chain on every next().
Using foreach, you visit each Branch or Node just once.
Edit: Not sure if this helps, but here is an example of eagerly locating the leaves but lazily producing the values. It would stackoverflow or be slower in older versions of Scala, but the implementation of chained ++ was improved. Now it's a flat chain that gets shorter as it's consumed.
sealed abstract class Tree extends Iterable[Int] {
def iterator: Iterator[Int] = {
def leafIterator(t: Tree): List[Iterator[Int]] = t match {
case Node(_) => t.iterator :: Nil
case Branch(left, right) => leafIterator(left) ::: leafIterator(right)
}
this match {
case n # Node(_) => Iterator.fill(1)(n.value)
case Branch(left # Node(_), right # Node(_)) => left.iterator ++ right.iterator
case b # Branch(_, _) =>
leafIterator(b).foldLeft(Iterator[Int]())((all, it) => all ++ it)
}
}
}
case class Branch(left: Tree, right: Tree) extends Tree {
override def toString = s"Branch($left, $right)"
}
case class Node(elem: Int) extends Tree {
def value = {
Console println "An expensive leaf calculation"
elem
}
override def toString = s"Node($elem)"
}
object Test extends App {
// many leaves
val n = 1024 * 1024
val ns: List[Tree] = (1 to n).map(Node(_)).toList
var b = ns
while (b.size > 1) {
b = b.grouped(2).map { case left :: right :: Nil => Branch(left, right) }.toList
}
Console println s"Head: ${b.head.iterator.take(3).toList}"
}
In this implementation, the topmost branch does NOT know how many elements there are in its left and right sub-branches.
Therefore, the iterator is built recursively with the divide and conquer approach which is clearly represented in the iterator method - you get to each node (case Branch), you produce the iterator of the single node case Node => ... and then you join them.
Without getting into each and every node, it would not know what elements there are and how the tree is structured (odd branches allowed vs not allowed etc.).
EDIT:
Let's have a look inside the ++ method on Iterator.
def ++[B >: A](that: => GenTraversableOnce[B]): Iterator[B] = new Iterator.JoinIterator(self, that)
and then at Iterator.JoinIterator
private[scala] final class JoinIterator[+A](lhs: Iterator[A], that: => GenTraversableOnce[A]) extends Iterator[A] {
private[this] var state = 0 // 0: lhs not checked, 1: lhs has next, 2: switched to rhs
private[this] lazy val rhs: Iterator[A] = that.toIterator
def hasNext = state match {
case 0 =>
if (lhs.hasNext) {
state = 1
true
} else {
state = 2
rhs.hasNext
}
case 1 => true
case _ => rhs.hasNext
}
def next() = state match {
case 0 =>
if (lhs.hasNext) lhs.next()
else {
state = 2
rhs.next()
}
case 1 =>
state = 0
lhs.next()
case _ =>
rhs.next()
}
override def ++[B >: A](that: => GenTraversableOnce[B]) =
new ConcatIterator(this, Vector(() => that.toIterator))
}
From that we can see that joining iterators just creates a recursive structure in the rhs field. Furthermore, let's focus on it a bit more.
Consider an even tree with structure level1 [A]; level2 [B][C]; level 3[D][E][F][F]
When you call JoinIterator on the iterator you preserve the existing lhs iterator. However, you always .toIterator on rhs. Which means that for each subsequent level, the rhs part will be reconstructed. So for B ++ C you get that looks like A.lhs (stands for B) and A.rhs (stands for C.toIterator) where C.toIterator stands for C.lhs and C.rhs etc. Thus, the added complexity.
I hope this answers your question.
This question already has answers here:
What is the Ruby <=> (spaceship) operator?
(6 answers)
Closed 7 years ago.
If we have <, >, and ==, the total order is determined by those. Why do we need <=>?
we Don't need <=>.
a<=>b
is equivalent to:
if a<b
return -1
elsif a>b
return 1
else
return 0
end
It is there for convenience and it was taken from perl.
<=> is the basis of Comparable, so you don't have to implement all of the compare functions yourself. It's easier and less error-prone to just implement one function instead of three.
The "spaceship" operator is for comparison, not equality. It's similar in concept to C's strcmp function.
From the String class:
string <=> other_string → -1, 0, +1 or nil
Comparison — Returns -1, 0, +1 or nil depending on whether string is less than, equal to, or greater than other_string.
In short, == returns a boolean expressing equality, while <=> returns a number expressing comparative value. If the first object is of greater value than the second, <=> returns +1. If it's of lesser value, -1 is returned. If the two have the same value, 0 is returned.
The "value" of an object can be defined to be just about anything. For String, however, <=> checks the lexicographic ordering of the two arguments.
Therefore:
"abc" == "abc" # true
("abc" <=> "abc") == 0 # true
You are correct that there is redundancy between <, ==, > and <=>. In fact, when <, ==, > are defined, <=> is automatically defined.
This operator is sometimes called a "signum" function. It provides the most concise way to customize sort order. For example:
require "ostruct"
# Fake "rows" with OpenStructs
my_data = [
OpenStruct.new({ :name => "Ben", :age => 50 }),
OpenStruct.new({ :name => "Abe", :age => 50 }),
OpenStruct.new({ :name => "Cab", :age => 51 })
]
# Sort by age descending, then name ascending
puts my_data.sort { |a, b| 2 * (b.age <=> a.age) + (a.name <=> b.name) }
This works because the value from <=> is always -1, 0, or 1. I don't know of a more efficient way to do general-purpose sorting.
Beginning programmer here, just wanting to understand the process behind Ruby's sort method when using the spaceship operator <=>. Hope someone can help.
In the following:
array = [1, 2, 3]
array.sort { |a, b| a <=> b }
... I understand that sort is comparing a pair of numbers at a time and then returning -1 if a belongs before b, 0 if they're equal, or 1 if a should follow b.
But in the case of sorting in descending order, like so:
array.sort { |a, b| b <=> a }
... what exactly is happening? Does sort still compare a <=> b and then flip the result? Or is it interpreting the returns of -1, 0 and 1 with reversed behavior?
In other words, why does placing the variables in the block like so:
array.sort { |b, a| b <=> a }
...result in the same sorting pattern as in the first example?
a <=> b will return -1 if a belongs before b, 0 if they're equal, or 1 if a should follow b.
b <=> a will return -1 if b belongs before a, 0 if they're equal, or 1 if b should follow a.
Since you are reversing the order, the output should be reversed, just like the - operator, for example. 3-5 is -2, and 5-3 is 2.
array.sort { |b, a| b <=> a } is equal to array.sort { |a, b| a <=> b } because the first argument is before the spaceship, and the second is after. Ruby doesn't care what the name of the variable is.
Sort just does this:
comparison_block.call(elem[i],elem[j])
It doesn't know or care what your block looks like internally, but it knows which element it passed in as the first argument and which as the second, and that's what the result is based on. In a normal numeric ascending sort, calling the block with (1,0) should return 1; calling it with (0,1) should return -1. Order matters.
Here is working code:
p = (10..14).map { |a|
(a..14).map { |b|
a * b
}
flatten.select { |p|
p.to_s == p.to_s.reverse
}
But I want to keep the information about 'a' and 'b' which produced 'a * b':
p = (10..14).map { |a|
(a..14).map { |b|
[a, b, a * b, '=']
}
}.select { |v|
v[2].to_s == v[2].to_s.reverse
}
puts p
This code print not palindromic number. I guess that reason is a Array flatten. How do I need to change code to get palindromic number?
You want to flatten only one level, so use flatten(1) instead.
(10..14).map { |a|
(a..14).map { |b|
[a, b, a * b, '=']
}
}.flatten(1).select { |v|
v[2].to_s == v[2].to_s.reverse
}
Alternatively, replace your outer map by flat_map:
(10..14).flat_map { |a|
(a..14).map { |b|
[a, b, a * b, '=']
}
}.select { |v|
v[2].to_s == v[2].to_s.reverse
}
Note: flatten takes an argument since Ruby 1.8.7. flat_map is new to Ruby 1.9.2. Make sure you have the right version, or require "backports/1.9.2/enumerable/flat_map" or require "backports/1.8.7/array/flatten".
Yes, since you don't call flatten in the second version, the array is too nested and the select doesn't work because v is a row of elements, not a single elements. However if you'd just call flatten like in the first version, the resulting array would be too flat.
There are multiple ways to solve this:
In 1.9.2 you can replace the outer call to ´mapwithflat_mapwhich works likemap`, but automatically produces a flat array.
In 1.8.7+ you can call flatten(1) instead of flatten, which will flatten the array by exactly one level of nesting, resulting in the structure you want.
Instead of an array you could define a class to represent a number, which avoids the problem of accidentally flattening the inner arrays lets you access the properties of the number more meaningfully.
Option 3 could look like this:
Product = Struct.new(:factor1, :factor2) do
def product
factor1 * factor2
end
def to_s
"#{factor1} * #{factor2} = #{product}"
end
end
products = (10..14).map { |a|
(a..14).map { |b|
Product.new(a,b)
}
}.flatten.select { |prod|
prod.product.to_s == prod.product.to_s.reverse
}
puts products
palindromes = []
(10..14).each do |a|
(a..14).each do |b|
p = (a * b).to_s
palindromes << [a,b] if p == p.reverse
end
end
puts palindromes.join(',')
wesbailey#feynman:~/code_katas> ruby palindrome.rb
11,11
let's say when I'm comparing values in ruby, i have a value in mind that no matter what I want, using sort on that value and anything else returns a -1 (so this value is default sorted as smaller than everything).
for example, let's say i want '100' to sort smaller 100% of the time against 99. so that if i'm sorting values in an array, and a comparison comes up between 100 and 99, 100 is sorted smaller (ie, -1 is returned). but, i want all the other cases to be normal (98 is smaller than 99, 50 is bigger than 30, etc)
edit: okay this is what i want
if i have an x and a y, i do not want to use
x <=> y
i want to use (in pseudocode and hand-wavy-ness)
x > y
which means, this x is always greater than this y
Why don't you instead use a dictionary to keep values associated with their relative value? In this case, the string abc can be mapped to -1, and then just make sure no other values map to values equal to or less than -1.
Edit: If you're only concerned with one particular value breaking the norm, then this solution is not for you.
Easier to handle the specialness outside of the sort!
module Enumerable
def sort_excluding(*vals)
special,rest = partition {|x| vals.include?(x)}
rest.sort + special
end
end
One way to do it would be to implement a derivative class for your custom comparisons (http://www.ruby-doc.org/core/classes/Comparable.html)
Here's some sample code (and tests) for you:
class StrA < String
include Comparable
attr :str
def <=>(anOther)
if (str == "abc" && anOther.str == "abc")
0
elsif (str == "abc")
-1
elsif (anOther.str == "abc")
1
else
str <=> anOther.str
end
end
def initialize(str)
#str = str
end
def inspect
#str
end
end
And the tests:
a = StrA.new("Z")
b = StrA.new("B")
c = StrA.new("abc")
d = StrA.new("")
a > b # 1
a > c # 1
c > a # -1
d > c # 1
c > d # -1
c < d # 1
c > d # -1
[a, b, c, d].sort! # [ "Z", "B", "", "abc"]
I think what you want is:
[30, 50, 4, 0, 100, -22, 99].sort_by {|a| [a == 100 ? -1 : 0, a ]}.reverse
which gives:
99
50
30
4
0
-22
100
Hope I understood the question!
Array#sort or Enumerable#sort(I don't know what you are trying to sort) can take an obtional block. If the block is given, the block is used for comparison instead of <=>
For example this code will sort reversed:
foo.sort { |a,b| b <=> a }
In your case you need to call #sort something like the following:
foo.sort do |a,b|
if [a,b].sort == [99,100]
b-a # returns 1 or -1 so that 99 > 100
else
a <=> b
end
end
I am not entirely sure about the way you are trying to sort, but this should enable you to use sort in the manner you need. More inforamtion about Array#sort, and any other method can be found on your linux(and possible other OS's) via ri like this: ri Array#sort.
You could override the sort method for the object like so:
class Fixnum
alias old_sort <=>
def <=>(object)
if (self == 100 or object == 100)
return -1
else
return self.old_sort object
end
end
end
puts (101 <=> 100)
Edit 1: Above is a fully working example.
Edit 2: As stated by johannes in the comments, you really shouldn't implement this exact code. This is merely a simple example of how to override your sorting method to do domain-specific logic. Also, updated the code to reflect johannes' other comment about comparing with both object and self.