转载自: http://zonov.me/golang-for-rubyists-part-7-ruby-and-golang-methods-comparison/ 已获原作者授权
原标题: Golang for Rubyists. Part 7. Ruby and Golang, methods comparison
Array
Let’s start with something simple:
2.2.1 :001 > array = [1,3,5,7]
=> [1, 3, 5, 7]
2.2.1 :002 > array.shift
=> 1
2.2.1 :003 > array
=> [3, 5, 7]
2.2.1 :004 > array.push(1)
=> [3, 5, 7, 1]
2.2.1 :005 > array.unshift(array.pop)
=> [1, 3, 5, 7]
So, we used four methods here: shift, unshift, push, pop. Now let me try to do something similar in Golang:
package main
import ("fmt")
func main() {
// Initialize the slice
array := []int{1, 3, 5, 7}
fmt.Println(array)
// Shift
x := array[0]
fmt.Println(x)
array = array[1:]
fmt.Println(array)
// Push
array = append(array, x)
fmt.Println(array)
// Pop and Unshift
x, array = array[len(array)-1], array[:len(array)-1]
array = append([]int{x}, array...)
fmt.Println(array)
}
(
https://play.golang.org/p/wNgO9LeX514)
Not so short and expressive, huh? But still pretty straightforward. The only confusion I can anticipate is the last example, with pop and unshift. Just to explain, at first we assign to x the last element of an array and we modify the array variable to be everything starting from the element at index 0 to the pre-last one. And at the next line we create a new slice with just x and append an array to it.
Also, you could notice here, that usage of [] to access an array/slice element is exactly the same as in Ruby (or most of the languages).
Let’s move on to the more solid examples:
2.2.1 :001 > array = [1, 3, 5, 7]
=> [1, 3, 5, 7]
2.2.1 :002 > array.map { |a| a + 1 }
=> [2, 4, 6, 8]
2.2.1 :003 > array
=> [1, 3, 5, 7]
2.2.1 :005 > array.reject { |a| a % 3 == 0}
=> [1, 5, 7]
2.2.1 :006 > array
=> [1, 3, 5, 7]
2.2.1 :007 > array.sample
=> 7
2.2.1 :008 > array
=> [1, 3, 5, 7]
2.2.1 :009 >
2.2.1 :010 > array.min
=> 1
2.2.1 :011 >
2.2.1 :012 > array.max
=> 7
2.2.1 :014 > array.shuffle
=> [5, 7, 3, 1]
Here we iterate through a collection, without original collection modification, then we reject some value also without a modification, then we get a random element from it, then we have a basic min/max methods and a shuffle method, which shuffles an array elements. Are you optimistic about Golang abilities here? Let it a try! Nope, let me just tell you, that Go is not a functional language. Not at all. Not for an iota. The only way you can achieve map/reduce/filter functionality is by writing for loops. Also, there are no built-in min/max functions, unfortunately. Surprisingly I was able to find a shuffle implementation:
package main
import (
"fmt"
"math/rand"
)
func main() {
// Initialize the slice
array := []int{1, 3, 5, 7}
fmt.Println(array)
rand.Shuffle(len(array), func(i, j int) {
array[i], array[j] = array[j], array[i]
})
fmt.Println(array)
}
Btw it still uses two for loops under the hood:
https://golang.org/src/math/rand/rand.go#L232. Interesting to notice here is that it’s possible to pass a function to a method in Golang.
Just to give an impression, here is the possible implementation of our own Filter function in Golang:
package main
import ("fmt")
func main() {
array := []int{1, 3, 5, 7}
filteredArray := Filter(array, func(i int) bool {
return i%3 != 0
})
fmt.Println(filteredArray)
}
func Filter(in []int, fn func(int) bool) []int {
out := make([]int, 0, len(in))
for i := 0; i < len(in); i++ {
current := in[i]
if fn(current) {
out = append(out, current)
}
}
return out
}
(
https://play.golang.org/p/LFvueXzO-BU. Here is a version, using interfaces, more generic, but still far from ideal
https://play.golang.org/p/N9JvUXo7ugq)
As you can see, this will work only with slices of integers. In the link above you can find a more generic implementation. It uses reflection mechanism of Golang, which we didn’t explore yet.
So, this function may look cumbersome, but let’s take a look onto Ruby’s map method under the hood:
static VALUE
rb_ary_collect(VALUE ary)
{
long i;
VALUE collect;
RETURN_SIZED_ENUMERATOR(ary, 0, 0, ary_enum_length);
collect = rb_ary_new2(RARRAY_LEN(ary));
for (i = 0; i < RARRAY_LEN(ary); i++) {
rb_ary_push(collect, rb_yield(RARRAY_AREF(ary, i)));
}
return collect;
}
Looks pretty similar, isn’t it? Sure, in Ruby we don’t work with types – that’s the only difference. So, long story short, use for loops, that’s the way to go.
But also one thing I want to mention is that there is a more convenient way to iterate through a collection, here how it looks like (works for both maps and arrays):
package main
import ("fmt")
func main() {
array := []int{1, 3, 5, 7}
for i := range array {
fmt.Println(i * 2)
}
}
(
https://play.golang.org/p/6g28EUCvQ6f)
Hashes
One of the most heavily used data structures in Ruby is Hash.
Just kiddin, I have no idea, which is the most heavily used one. Let’s take a look at these examples, where we apply a couple of useful functions to it:
2.2.1 :001 > hash = { foo: 'bar', fizz: 'buzz', berlin: 'rain', munich: 'beer' }
=> {:foo=>"bar", :fizz=>"buzz", :berlin=>"rain", :munich=>"beer"}
2.2.1 :003 >
2.2.1 :004 > hash.values
=> ["bar", "buzz", "rain", "beer"]
2.2.1 :005 > hash.keys
=> [:foo, :fizz, :berlin, :munich]
2.2.1 :006 >
2.2.1 :008 > hash.values_at(:munich, :berlin)
, > ["beer", "rain"]
Nothing magical, just retrieving all values, all keys, and values by multiple keys, should be easy-peasy?
package main
import ("fmt")
func main() {
hash := map[string]string{"foo": "bar", "fizz": "buzz", "berlin": "rain", "munich": "beer"}
fmt.Println(hash)
values := make([]string, 0, len(hash))
for _, value := range hash {
values = append(values, value)
}
fmt.Println(values)
keys := make([]string, 0, len(hash))
for key := range hash {
keys = append(keys, key)
}
fmt.Println(keys)
valuesAt := []string{hash["munich"], hash["berlin"]}
fmt.Println(valuesAt)
}
(
https://play.golang.org/p/YN0xnhly-r9)
I am honestly not sure about the last one, probably there is a way to make it more readable. But anyway, beauty of Go in it’s straightforwardness