Skip to content

Instantly share code, notes, and snippets.

@jules27
Last active October 13, 2021 14:46
Show Gist options
  • Select an option

  • Save jules27/3668380 to your computer and use it in GitHub Desktop.

Select an option

Save jules27/3668380 to your computer and use it in GitHub Desktop.

Revisions

  1. jules27 revised this gist Apr 7, 2021. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion ruby_end_keyword.md
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,6 @@
    ## Introduction

    After working through exercises for learning Ruby as part of preparation for the upcoming <a href="http://devbootcamp.com">Dev Bootcamp</a>, we had a friendly discussion about the exercise to implement a <a href="http://socrates.devbootcamp.com/exercises/31">Reverse Polish notation calculator</a>. At the end of discussion, one of the staff members, Jesse, posted his solution to students.
    After working through exercises for learning Ruby as part of preparation for the upcoming Dev Bootcamp (note: no longer exists), we had a friendly discussion about the exercise to implement a Reverse Polish notation calculator. At the end of discussion, one of the staff members, Jesse, posted his solution to students.

    The solution is well-designed, clean, and easy to read. However, one line of code caught my eye:

  2. jules27 revised this gist Sep 7, 2012. 1 changed file with 16 additions and 12 deletions.
    28 changes: 16 additions & 12 deletions ruby_end_keyword.md
    Original file line number Diff line number Diff line change
    @@ -35,7 +35,7 @@ p some_other_value # => [13, 12, 11]

    This is pretty straightforward. `some_value` is constructed as expected, and `#reverse` does its job of reversing the resulting array. `some_other_value` contains `end.reverse`, and we see that line does essentially the same thing as `some_value.reverse`.

    It appears that you can call array methods on the `end` keyword, and that method gets called once at the end of the loop. Let's try some more things.
    It appears that you can call array methods on the `end` keyword, or more accurately, call methods on the object returned by the `end` keyword, and that method gets called once at the end of the loop. Let's try some more things.

    ```ruby
    first_element = [1,2,3].each do
    @@ -57,7 +57,7 @@ middle_element = [1,2,3].each do
    p middle_element # => 2
    ```

    Hey, that's pretty neat. So does `end` essentially behave like an array?
    Hey, that's pretty neat. So does `end` actually return an array?

    ### Calling Object methods

    @@ -80,24 +80,28 @@ This confirms that `end` is an array in this definition, and that it's also poss
    What about `?` and `!` methods? (for lack of better word)

    ```ruby
    reversed = [1,2,3].each do
    a = [1,2,3]

    reversed = a.each do
    end.reverse
    p reversed # [3, 2, 1]
    p reversed # => [3, 2, 1]
    p a # => [1, 2, 3]

    reversed_again = [1,2,3].each do
    reversed_again = a.each do
    end.reverse!
    p reversed_again # [3, 2, 1]
    p reversed_again # => [3, 2, 1]
    p a # => [3, 2, 1]

    am_i_empty = [1,2,3].each do
    am_i_empty = a.each do
    end.empty?
    p am_i_empty # false
    ```

    It seems that `end.reverse` and `end.reverse!` have the same result, which makes sense because the methods are called at the end of the loop, and the resulting array is returned.
    While it seems that `end.reverse` and `end.reverse!` have the same result, the `reverse` method has left the variable `a` untouched, and the `reverse!` method has modified it.

    ### Daisy-chaining of calling methods using `end`

    It's also possible to continuously call methods using `end` in a daisy-chain fashion.
    It's also possible to continuously call methods using the object returned by `end` in a daisy-chain fashion.

    ```ruby
    chained_map = [1,2,3].map do |int|
    @@ -110,13 +114,13 @@ chained_map = [1,2,3].map do |int|
    p chained_map # => [33, 32, 31]
    ```

    In this example, each iteration increases each value in the array by 10, so at the end of the third iteration, we get `[31, 32, 33]`. The last `end.reverse` reverse that result, so at the very end, we get `[33, 32, 31]`.
    In this example, each iteration increases each value in the array by 10, so at the end of the third iteration, we get `[31, 32, 33]`. The last `end.reverse` reverse that result, so by the last iteration, we get `[33, 32, 31]`.

    ### Checking the returned class type

    Most of what I've written so far has been applying the `end` keyword to the Array object, but we can certainly do the same for other objects.

    When I first raised the question, I wondered if the reason that you can call Array methods on `end` is because the starting object (`inject([])`) is an Array. The correct answer is that methods called on `end` operates on the **ending object**, not the starting object.
    When I first raised the question, I wondered if the reason that you can call Array methods on the object returned by `end` is because the starting object (`inject([])`) is an array. The correct answer is that methods called on the object returned by `end` operates on the **ending object**, not the starting object.

    ```ruby
    type_test = [1,2,3].inject(Hash.new(0)) do |result, element|
    @@ -139,6 +143,6 @@ In the first definition, `type_test` has the resulting value of `{}`, an empty h

    ## Conclusion

    I never imagined that you can call methods on the `end` keyword. Jesse says: every valid Ruby expression returns something. You can assign that something to a variable, call methods on it, etc.
    I never imagined that you can call methods on the `end` keyword, or more precisely, on the object returned by the `end` keyword. Jesse says: every valid Ruby expression returns something. You can assign that something to a variable, call methods on it, etc.

    I hope this gist was helpful to some of you. Thanks for reading!
  3. jules27 revised this gist Sep 7, 2012. 1 changed file with 12 additions and 8 deletions.
    20 changes: 12 additions & 8 deletions ruby_end_keyword.md
    Original file line number Diff line number Diff line change
    @@ -1,8 +1,8 @@
    ## Introduction

    After working through exercises for learning Ruby as part of preparation for the upcoming <a href="http://devbootcamp.com">Dev Bootcamp</a>, we had a friendly discussion about the exercise to implement a <a href="http://socrates.devbootcamp.com/exercises/31">Reverse Polish notation calculator</a>. At the end of discussion, one of the staff members, Jesse, posted his solution to show the students.
    After working through exercises for learning Ruby as part of preparation for the upcoming <a href="http://devbootcamp.com">Dev Bootcamp</a>, we had a friendly discussion about the exercise to implement a <a href="http://socrates.devbootcamp.com/exercises/31">Reverse Polish notation calculator</a>. At the end of discussion, one of the staff members, Jesse, posted his solution to students.

    As expected, the solution is well-designed, clean, and easy to read. However, one line in his code caught my eye:
    The solution is well-designed, clean, and easy to read. However, one line of code caught my eye:

    ```ruby
    tokenize(array).inject([]) do |stack, token|
    @@ -57,7 +57,7 @@ middle_element = [1,2,3].each do
    p middle_element # => 2
    ```

    Hey, that's pretty neat. So `end` essentially behaves like an array now?
    Hey, that's pretty neat. So does `end` essentially behave like an array?

    ### Calling Object methods

    @@ -73,7 +73,7 @@ my_id = [1,2,3].each do
    p my_id # => 70236355699360 (result is different every time)
    ```

    This confirms our hypothesis that `end` is an array, and that it's also possible to call methods from its parent class.
    This confirms that `end` is an array in this definition, and that it's also possible to call methods from its parent class.

    ### Calling `?` and `!` methods

    @@ -95,7 +95,7 @@ p am_i_empty # false

    It seems that `end.reverse` and `end.reverse!` have the same result, which makes sense because the methods are called at the end of the loop, and the resulting array is returned.

    ### Daisy-chaining calling methods using `end`
    ### Daisy-chaining of calling methods using `end`

    It's also possible to continuously call methods using `end` in a daisy-chain fashion.

    @@ -114,7 +114,9 @@ In this example, each iteration increases each value in the array by 10, so at t

    ### Checking the returned class type

    When I first raised the question, I wondered if the reason that you can call Array methods on `end` is because the starting object (`inject([])`) is an Array. The correct answer is that methods called on `end` operates on the **ending object**.
    Most of what I've written so far has been applying the `end` keyword to the Array object, but we can certainly do the same for other objects.

    When I first raised the question, I wondered if the reason that you can call Array methods on `end` is because the starting object (`inject([])`) is an Array. The correct answer is that methods called on `end` operates on the **ending object**, not the starting object.

    ```ruby
    type_test = [1,2,3].inject(Hash.new(0)) do |result, element|
    @@ -129,9 +131,11 @@ p type_test_again # => Fixnum

    ```

    Here we see that even though both definitions have the same starting object `Hash`, the resulting classes are different. In the first definition, `type_test` has the resulting value of `{}`, an empty hash, and thus returns the class `Hash`. The second definition `type_test_again` has the resulting value of `3`, so it returns the class `Fixnum`.
    Here we see that even though both definitions have the same starting object `Hash`, the resulting classes are different.

    In the first definition, `type_test` has the resulting value of `{}`, an empty hash, and thus returns the class `Hash`. The second definition `type_test_again` has the resulting value of `3`, so it returns the class `Fixnum`.

    Thanks to Arne for pointing this out!
    (Thanks to Arne for pointing this out!)

    ## Conclusion

  4. jules27 created this gist Sep 7, 2012.
    140 changes: 140 additions & 0 deletions ruby_end_keyword.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,140 @@
    ## Introduction

    After working through exercises for learning Ruby as part of preparation for the upcoming <a href="http://devbootcamp.com">Dev Bootcamp</a>, we had a friendly discussion about the exercise to implement a <a href="http://socrates.devbootcamp.com/exercises/31">Reverse Polish notation calculator</a>. At the end of discussion, one of the staff members, Jesse, posted his solution to show the students.

    As expected, the solution is well-designed, clean, and easy to read. However, one line in his code caught my eye:

    ```ruby
    tokenize(array).inject([]) do |stack, token|
    <...>
    end.pop
    ```

    You can do *what* with the `end` keyword?

    I was given some pointers to ponder about, so I spent some time playing with what we can do with `end`.

    ## Exploring the `end` keyword

    ### Calling Array methods

    First, compare these two array definitions:

    ```ruby
    some_value = [1,2,3].map do |int|
    int + 10
    end
    p some_value # => [11, 12, 13]
    p some_value.reverse # => [13, 12, 11]

    some_other_value = [1,2,3].map do |int|
    int + 10
    end.reverse
    p some_other_value # => [13, 12, 11]
    ```

    This is pretty straightforward. `some_value` is constructed as expected, and `#reverse` does its job of reversing the resulting array. `some_other_value` contains `end.reverse`, and we see that line does essentially the same thing as `some_value.reverse`.

    It appears that you can call array methods on the `end` keyword, and that method gets called once at the end of the loop. Let's try some more things.

    ```ruby
    first_element = [1,2,3].each do
    end.first
    p first_element # => 1

    last_element = [1,2,3].each do
    end.last
    p last_element # => 3
    ```

    These results are also pretty straightforward, as `#first` and `#last` are both array methods.

    Let's try a different notation:

    ```ruby
    middle_element = [1,2,3].each do
    end[1]
    p middle_element # => 2
    ```

    Hey, that's pretty neat. So `end` essentially behaves like an array now?

    ### Calling Object methods

    Let's try some methods that are not specific to the `Array` class.

    ```ruby
    my_class = [1,2,3].each do
    end.class
    p my_class # => Array

    my_id = [1,2,3].each do
    end.object_id
    p my_id # => 70236355699360 (result is different every time)
    ```

    This confirms our hypothesis that `end` is an array, and that it's also possible to call methods from its parent class.

    ### Calling `?` and `!` methods

    What about `?` and `!` methods? (for lack of better word)

    ```ruby
    reversed = [1,2,3].each do
    end.reverse
    p reversed # [3, 2, 1]

    reversed_again = [1,2,3].each do
    end.reverse!
    p reversed_again # [3, 2, 1]

    am_i_empty = [1,2,3].each do
    end.empty?
    p am_i_empty # false
    ```

    It seems that `end.reverse` and `end.reverse!` have the same result, which makes sense because the methods are called at the end of the loop, and the resulting array is returned.

    ### Daisy-chaining calling methods using `end`

    It's also possible to continuously call methods using `end` in a daisy-chain fashion.

    ```ruby
    chained_map = [1,2,3].map do |int|
    int + 10 # => [11, 12, 13]
    end.map do |int|
    int + 10 # => [21, 22, 23]
    end.map do |int|
    int + 10 # => [31, 32, 33]
    end.reverse
    p chained_map # => [33, 32, 31]
    ```

    In this example, each iteration increases each value in the array by 10, so at the end of the third iteration, we get `[31, 32, 33]`. The last `end.reverse` reverse that result, so at the very end, we get `[33, 32, 31]`.

    ### Checking the returned class type

    When I first raised the question, I wondered if the reason that you can call Array methods on `end` is because the starting object (`inject([])`) is an Array. The correct answer is that methods called on `end` operates on the **ending object**.

    ```ruby
    type_test = [1,2,3].inject(Hash.new(0)) do |result, element|
    result
    end.class
    p type_test # => Hash

    type_test_again = [1,2,3].inject(Hash.new(0)) do |result, element|
    result = element
    end.class
    p type_test_again # => Fixnum

    ```

    Here we see that even though both definitions have the same starting object `Hash`, the resulting classes are different. In the first definition, `type_test` has the resulting value of `{}`, an empty hash, and thus returns the class `Hash`. The second definition `type_test_again` has the resulting value of `3`, so it returns the class `Fixnum`.

    Thanks to Arne for pointing this out!

    ## Conclusion

    I never imagined that you can call methods on the `end` keyword. Jesse says: every valid Ruby expression returns something. You can assign that something to a variable, call methods on it, etc.

    I hope this gist was helpful to some of you. Thanks for reading!