How to Access Return Value of a Method Chain in Ruby?
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, thestr.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')}
精彩评论