Sort numbers in vimscript - sorting

Experimenting with vimscript and reading the wonderful Learn Vimscript the Hard Way (LVTHW), I realized that Vim wasn't sorting numbers the way I wanted it to.
For example this function from LVTHW
function! Sorted(l)
let new_list = deepcopy(a:l)
call sort(new_list)
return new_list
endfunction
surprised me when I called it with Sorted([3, 1, 11, 2]):
It returned [1, 11, 2, 3].
I think Vim sorts those numbers in alphabetical order. But I'd expect the function to return the numbers in natural order:
[1, 2, 3, 11]
How can I convince Vim (7.4) to do that?

if you've read the help doc of sort() function, you 'll see that you can give a n to {func} parameter to let sort do numerical sorting:
example:
:echo sort([3,1,11,2],'n')
[1, 2, 3, 11]

The trick is to pass Vim's sort a comparator function, which in my case I called NaturalOrder.
" Sorts numbers in ascending order.
" Examples:
" [2, 3, 1, 11, 2] --> [1, 2, 2, 3, 11]
" ['2', '1', '10','-1'] --> [-1, 1, 2, 10]
function! Sorted(list)
" Make sure the list consists of numbers (and not strings)
" This also ensures that the original list is not modified
let nrs= ToNrs(a:list)
let sortedList = sort(nrs, "NaturalOrder")
echo sortedList
return sortedList
endfunction
" Comparator function for natural ordering of numbers
function! NaturalOrder(firstNr, secondNr)
if a:firstNr < a:secondNr
return -1
elseif a:firstNr > a:secondNr
return 1
else
return 0
endif
endfunction
" Coerces every element of a list to a number. Returns a new list without
" modifying the original list.
function! ToNrs(list)
let nrs = []
for elem in a:list
let nr = 0 + elem
call add(nrs, nr)
endfor
return nrs
endfunction

Related

Lucas Sequence in Ruby

The Lucas Sequence is a sequence of numbers. The first number of the sequence is 2. The second number of the Lucas Sequence is 1. To generate the next number of the sequence, we add up the previous two numbers. For example, the first six numbers of the sequence are: 2, 1, 3, 4, 7, 11, ...
Write a method lucasSequence that accepts a number representing a length as an arg. The method should return an array containing the Lucas Sequence up to the given length. Solve this recursively.
def lucas_sequence(length)
return [] if length == 0
return [2] if length == 1
return [2, 1] if length == 2
seq = lucas_sequence(length - 1)
next_el = seq[-1] + seq[-2]
seq << next_el
seq
end
p lucas_sequence(0) # => []
p lucas_sequence(1) # => [2]
p lucas_sequence(2) # => [2, 1]
p lucas_sequence(3) # => [2, 1, 3]
p lucas_sequence(6) # => [2, 1, 3, 4, 7, 11]
p lucas_sequence(8) # => [2, 1, 3, 4, 7, 11, 18, 29]
**I'm having a hard time understanding the recursion logic behind this. Can someone explain how the computer is solving this?
Does the computer read the length and then add up from [2,1] until it reaches its length? If so, how does it continuously count down? **
Recursion is the programming equivalent of mathematical induction. Given a series, assume that the problem is solved for the previous member of the series and provide the rule for generating this member.
So, consider just these lines:
def lucas_sequence(length)
seq = lucas_sequence(length - 1) # <1>
next_el = seq[-1] + seq[-2] # <2>
seq << next_el # <3>
seq # <4>
end
That says:
You want to know the lucas sequence of a certain length (length). Fine, first tell me the previous lucas sequence, the sequence that is one unit shorter than this (length-1). (That is the recursion: the lucas_sequence method, itself, calls the lucas_sequence method, but with a reduced length value.)
Add up the last two members of that shorter sequence...
...and append the sum to that shorter sequence...
...and the result is this sequence, the one you asked for.
And that's basically all there is to it! The only problem is that there is no place to start. We assume that for the seq of length 4, we have solved 3 already, which we get by assuming that we have solved 2 already, which we get by assuming we have solve 1 already... But we haven't actually solved any of those!
So we begin by backstopping the most degenerate cases:
return [] if length == 0
return [2] if length == 1
return [2, 1] if length == 2
Now the problem is solved if length is 0, 1, or 2, because we just give those answers directly. Okay, so if length is 3, we solve with reference to 2, which is known. Okay, if length is 4, we solve with reference to 3, and I just told you how to do that. Okay, if length is 5, we solve with reference to 4, and I just told you how to do that. And so on, for any length you care to give me.
So it is essentially a modified Fibonacci sequence. Best way to solve most structured sequences is with an Enumerator e.g.
lucas = Enumerator.new do |y|
a,b = 2,1
loop do
y << a
a, b = b, a + b
end
end
Then
lucas.first(10)
#=> [2, 1, 3, 4, 7, 11, 18, 29, 47, 76]
First we create a new Enumerator and then assign a and b to your starting values (2 and 1 respectively).
To generate the sequence we use a loop which will lazily yield the values to the yielder (y).
Here we push in a then we assign a to bs value and bs value to a + b in parallel to avoid overwriting a before the addition of a + b.

Find vector of unique elements and vector of repeats using for loops and if conditions

I want a program that takes a vector of integers and creates two new vectors, one which contains each unique element, and another which contains the number of times these unique elements were repeated in the original vector.
So,
[1, 2, 2, 3, 3, 3]
has unique elements:
[1, 2, 3]
with the following number of repeats:
[1, 2, 3]
I know this has been asked before, but I want to achieve this using purely for loops and if conditions if need be. For the same reason, the code can be given in pseudocode obviously.
Please be nice, I am literally a beginner who learned programming syntax a few days ago.
Here a solution
var arr = [1, 2, 2, 2, 3, 3, 3, 3]
var unique = [...new Set(arr)]
var repeated = []
var i = 0
unique.map((a) => {
arr.map((b) => {
if(a === b) i++
})
repeated.push(i)
i = 0
})
console.log("[" + arr.join(",") + "] has unique elements [" + unique.join(",") + "] with the following number of repeats [" + repeated.join(",") + "]")

Return an array between start A and B

$array = []
def range(start_position,end_position)
for i in start_position..end_position
$array.push(i)
puts $array
end
return $array
end
range(1,10)
I was wondering why exactly my array isnt returning. Clearly when I do puts $array, 1-10 is being inserted, but when I call my function I want the array to be returned. Any thoughts, I'm reading through documentation but can't find what i've done wrong or if I have made any syntax errors.
It returns an array, but you are not doing anything with it, so it is discarded. You could print it (with p range(1,10) or puts range(1,10) ) or assign it to a variable for later use ( result = range(1,10) ). Which is the same as result = (1..10).to_a, not using your method at all.
Your code is essentially correct you just need to omit the puts $array within your for block. And also no need for the optional return keyword. So fixing your code we would have:
$array = []
def range(start_position,end_position)
for i in start_position..end_position
$array.push(i)
end
$array
end
p range(1,10) #=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Kernel#p performs the Kernel#inspect method which as stated in the documentation provides a human-readable representation of the object in question. In Ruby it would be much better to do something like this perhaps:
def range(start_position,end_position)
(start_position..end_position).map {|i| i }
end
p range(1,10) #=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Your array does return, but you don't do anything with the return value. Try saving it to a variable or print the result:
print range(1, 3) # => [1, 2, 3]
If you're looking for a simpler way to implement this function, you could use Ruby ranges to do it, something like:
def range (start_position, end_position)
# Create a range and convert it to an array
# We can omit return
(start_position .. end_position).to_a
end
print range(1, 3) # => [1, 2, 3]
Also beware your function will only work the first time, because you're using a global variable as your starting point. Check what happens when you call it multiple times:
print range(1, 3) # => [1, 2, 3]
print range(1, 3) # => [1, 2, 3, 1, 2, 3]

How do I slice an array into arrays of different sizes and combine them?

I want something as simple as the pseudocode below:
ar = [4,5,6,7,8,9]
last = ar.length-1
s = ar[0, 1..3, last] #fake code
puts s
Expected output has no 8:
4,5,6,7,9
Error:
bs/test.rb:12:in `[]': wrong number of arguments (3 for 2) (ArgumentError)
I know that it can accept only two args. Is there a simple way to get what I need ?
You almost have it, you're just using the wrong method, you should be using Array#values_at instead of Array#[]:
ar.values_at(0, 1..3, -1)
# => [4, 5, 6, 7, 9]
Also note that in Ruby, indices "wrap around", so that -1 is always the last element, -2 is second-to-last and so on.
You can do it this way also -
ar = [4,5,6,7,8,9]
arr = []
arr << ar[0..0] #arr[0] will return a single value but ar[0..0] will return it in array.
arr << ar[1..3]
arr << ar[-1..-1]
on printing arr it will give following output -
[[4], [5, 6, 7], [9]]

Finding arrays within arrays

I have an array of hashes, some of which are subsets of others.
a = []
a << {Bob: 1, Mary: 2, Sue: 3}
a << {Bob:1}
a << {Phil: 2, Brian: 8}
a << {Bob: 1, Mary: 2, Sue: 3, Tony: 9}
I need to return an array of the unique super-sets which, in this case, would be:
{Bob: 1, Mary: 2, Sue: 3, Tony: 9}
{Phil: 2, Brian: 8}
I read "Ruby Array Comparison Tricks" however it doesn't offer what I require.
Is there a Ruby solution to compare arrays and identify sub-arrays?
I don't know a great algorithm for this, but the brute force solution is pretty simple in Ruby: use the - operator to find the complement of one array in another, then check if it's empty. With casting so it works with hashes too, the code is something like
def superset?(ary1, ary2)
ary1 != ary2 && (ary2.to_a - ary1.to_a) == []
end
def maximal_sets(arrays)
arrays.reject{ |ary2| arrays.any?{ |ary1| superset?(ary1, ary2) } }
end

Resources