开发者

How to Access Return Value of a Method Chain in Ruby?

开发者 https://www.devze.com 2023-03-24 09:59 出处:网络
for example str = \'f01288c2\' #a hexadecimal string bin = str.to_i(16).to_s(2).rjust(str.length + (64 - (str.length % 64)), \'0\')

for example

str = 'f01288c2' #a hexadecimal string
bin = str.to_i(16).to_s(2).rjust(str.length + (64 - (str.length % 64)), '0')

so the size of the binary s开发者_开发技巧tring is always the multiply of 64.

The problem here is, the str.length is the length before it's converted into binary. I need the length of the string after to_s(2). How do I access the return value of the to_s(2)?

Update I wonder if there is a one chain solution.


The one-line solution would use Kernel#tap and str.replace:

ruby > str = 'f01288c2' #a hexadecimal string
 => "f01288c2" 
ruby > bin = str.to_i(16).to_s(2).rjust(str.length + (64 - (str.length % 64)), '0')
 => "0000000000000000000000000000000011110000000100101000100011000010" 
ruby > bin = str.to_i(16).to_s(2).tap { |str| str.replace str.rjust(str.length + (64 - (str.length % 64)), '0') }
 => "11110000000100101000100011000010" 


You must split it to two lines:

str = 'f01288c2' #a hexadecimal string
len = str.to_i(16).to_s(2)
bin = len.rjust(str.length + (64 - (str.lenght % 64)), '0')


Do you need the value itself, or do you need it for length-calculation of the resulting bin?

Perhaps this solves you problem (but not your answer):

str = 'f01288c2' #a hexadecimal string
bin = "%0*b" % [str.length + (64 - (str.length % 64)),str.to_i(16)]

Based on my other 'answer' and some thoughts I made a unit-test for the problem and combined the answers. I found no correct one-liner. in method my_solution I have at least some code, where my test says ok. I hope me test design was correct ;)

gem 'test-unit'
require 'test/unit'

def original(str) #from https://stackoverflow.com/questions/6894901/how-to-access-return-value-of-a-method-chain-in-ruby
  str.to_i(16).to_s(2).rjust(str.length + (64 - (str.length % 64)), '0')
end
def whitequark_1(str) #accepted: https://stackoverflow.com/questions/6894901/how-to-access-return-value-of-a-method-chain-in-ruby/6894974#6894974
  str.to_i(16).to_s(2).tap { |str| str.replace str.rjust(str.length + (64 - (str.length % 64)), '0') }
end  
def whitequark_2(str) #accepted: https://stackoverflow.com/questions/6894901/how-to-access-return-value-of-a-method-chain-in-ruby/6894974#6894974
  str.to_i(16).to_s(2).rjust(str.length + (64 - (str.length % 64)), '0')
end
def yossi(str)  #https://stackoverflow.com/questions/6894901/how-to-access-return-value-of-a-method-chain-in-ruby/6894943#6894943
  len = str.to_i(16).to_s(2)
  #~ len.rjust(str.length + (64 - (str.lenght % 64)), '0')
  len.rjust(str.size + (64 - (str.size% 64)), '0')
end
def my_solution(str)  #
  size1 = ("%0b" % str.to_i(16).to_s).size
  size2 = 64 * ( size1 / 64 + [1, size1 % 64 ].min)
  "%0*b" % [size2, str.to_i(16)]
end

#Select the version you want to check
#~ alias :experiment :original #wrong
#~ alias :experiment :yossi
#~ alias :experiment :whitequark_1 #wrong with f01288c2_f01288c2
#~ alias :experiment :whitequark_2  #wrong with f_f01288c2_f01288c2
alias :experiment :my_solution

#Testcases for different Test-setups.
module MyTestcases
  def test_binary()
    assert_match( /\A[01]+\Z/, @bin)
  end
  def test_solution()
    pend "No solution defined #{@bin}" unless defined? @solution
    assert_equal( 0, @solution.size % 64)
    assert_equal( @bin.to_i(2), @solution.to_i(2))
    assert_equal( @str, @solution.to_i(2).to_s(16))
    assert_equal( @solution, @bin)
  end
  def test_multiply64()
    assert_equal( 0, @bin.size % 64, 'no multiply of 64')
  end
  def test_smallest64()
    size = ("%b" % @str.to_i(16)).size
    smallestsize = 0
    #determine smallest 
    while smallestsize < size
      smallestsize += 64
    end
    assert_equal( smallestsize, @bin.size, 'not smallest multiply of 64')
  end
end

class MyTest_00000001 < Test::Unit::TestCase
  def setup
    @str = '1' #a hexadecimal string
    @bin = experiment(@str)
    @solution = "0000000000000000000000000000000000000000000000000000000000000001"
  end
  include MyTestcases
end
class MyTest_f01288c2 < Test::Unit::TestCase
  def setup
    @str = 'f01288c2' #a hexadecimal string
    @bin = experiment(@str)
    @solution = "0000000000000000000000000000000011110000000100101000100011000010"
  end
  include MyTestcases
end
class MyTest_ff01288c2 < Test::Unit::TestCase
  def setup
    @str = 'ff01288c2' #a hexadecimal string
    @bin = experiment(@str)
    @solution = "0000000000000000000000000000111111110000000100101000100011000010"
  end
  include MyTestcases
end
class MyTest_f01288c2_f01288c2 < Test::Unit::TestCase
  def setup
    @str = 'f01288c2f01288c2' #a hexadecimal string
    @bin = experiment(@str)
    @solution = "1111000000010010100010001100001011110000000100101000100011000010"
  end
  include MyTestcases
end
class MyTest_f_f01288c2_f01288c2 < Test::Unit::TestCase
  def setup
    @str = 'ff01288c2f01288c2' #a hexadecimal string
    @bin = experiment(@str)
    @solution = "00000000000000000000000000000000000000000000000000000000000011111111000000010010100010001100001011110000000100101000100011000010"
  end
  include MyTestcases
end


Maybe I'm missing something, but you can access the return value of to_s(2) very easily by doing this:

r = str.to_i(16).to_s(2)


In real code you'd use an intermediate line to assign the lenght (see @Yossi's answer), it's the proper way to do it. Now, if you want a way to do a one-liner for fun, well, you can use for example the Object#as abstraction (which is in fact rather useful in some cases)

class Object
  def as
    yield self
  end
end

str = 'f01288c2'
len, bin = str.to_i(16).to_s(2).as { |len| [len, len.rjust(str.length + (64-(str.length%64)), '0') }


This is no 'answer', but a question - and it is a bit too long for a comment -soory.

so the size of the binary string is always the multiply of 64.

Your str.length + (64 - (str.length % 64)) should be a multiple of 64, so the binary string fits in?

For '11f01288c2' (10 characters) it should be 128? right? (64 is to small, so you need 128) But your calculation returns 64.

Is there a misunderstanding from my side?

Here a code example (and a quick version of another calculation - perhaps theres a better one):

str = '11f01288c2' #a hexadecimal string
p (str.length + (64 - (str.length % 64)))  #-> 64
p 64 * ((str.length * 8).divmod(64).first + 1) #-> 128
p 64 * ((str.length).divmod(8).first + 1) #-> 128


I think the calculation you want is:

(str.length / 64.0).ceil * 64

Yours will produce an unnecessary 64 0s on strings that are already an even multiple of 64 long.

The problem you're having is that you're trying to treat rjust like a block, so that you can get access to its receiver. But because it's a method, its arguments are evaluated before it is itself called. So the way to wedge this into a one-liner is to turn it around, creating an anonymous function and then calling it on your string:

lambda {|x| x.rjust((x.length / 64.0).ceil * 64, '0')}.call(str.to_i(16).to_s(2))

It's actually slightly puzzling to me that there's no built-in way to call a block on an object in Ruby, like tap but returning the result of the block. (Am I missing something?)

But of course we can easily create one ourselves:

class Object
  def doblock
    block_given? ? yield self : self
  end
end

and that would then let us do (among other things):

str.to_i(16).to_s(2).doblock {|x| x.rjust((x.length / 64.0).ceil * 64, '0')}
0

精彩评论

暂无评论...
验证码 换一张
取 消