Executable library
The general idea behind an executable library is to create a library that when used as a library does one thing; but has the ability to be run directly, where it will do some extra task using, but not limited to, functionality that it makes available as a library.
Task detail
- Create a library/module/dll/shared object/... for a programming language that contains a function/method called hailstone that is a function taking a positive integer and returns the Hailstone sequence for that number.
- The library, when executed directly should satisfy the remaining requirements of the Hailstone sequence task:
- 2. Use the routine to show that the hailstone sequence for the number 27 has 112 elements starting with 27, 82, 41, 124 and ending with 8, 4, 2, 1
- 3. Show the number less than 100,000 which has the longest hailstone sequence together with that sequences length.
- Create a second executable to calculate the following:
- Use the libraries hailstone function, in the standard manner, (or document how this use deviates from standard use of a library), together with extra code in this executable, to find the hailstone length returned most often for 1 <= n < 100,000"
- Explain any extra setup/run steps needed to complete the task.
Notes:
- It is assumed that for a language that overwhelmingly ships in a compiled form, such as C, the library must also be an executable and the compiled user of that library is to do so without changing the compiled library. I.e. the compile tool-chain is assumed not to be present in the runtime environment.
- Interpreters are present in the runtime environment.
Python
Executable libraries are common in Python. The Python entry for Hailstone sequence is already written in the correct manner.
The entry is copied below and, for this task needs to be in a file called hailstone.py
:
<lang python>def hailstone(n):
seq = [n] while n>1: n = 3*n + 1 if n & 1 else n//2 seq.append(n) return seq
if __name__ == '__main__':
h = hailstone(27) assert len(h)==112 and h[:4]==[27, 82, 41, 124] and h[-4:]==[8, 4, 2, 1] print("Maximum length %i was found for hailstone(%i) for numbers <100,000" % max((len(hailstone(i)), i) for i in range(1,100000)))</lang>
In the case of the Python language the interpreter maintains a module level variable called __name__. If the file hailstone.py is imported (as import hailstone
), then the __name__ variable is set to the import name of 'hailstone' and the if __name__ == '__main__'
expression would then be false, and only the hailstone function is available to the importer.
If the same file hailstone.py is run, (as maybe python hailstone.py
; or maybe double-clicking the hailstone.py file), then the __name__ variable is set to the special name of '__main__' and the if __name__ == '__main__'
expression would then be true causing its block of code to be executed.
Library importing executable
The second executable is the file common_hailstone_length.py
with this content:
<lang python>from collections import Counter
from hailstone import hailstone
def hailstone_length_frequency(hrange):
return Counter(len(hailstone(n)) for n in hrange).most_common()
if __name__ == '__main__':
upto = 100000 hlen, freq = hailstone_length_frequency(range(1, upto))[0] print("The length of hailstone sequence that is most common for\n" "hailstone(n) where 1<=n<%i, is %i. It occurs %i times." % (upto, hlen, freq))</lang>
Both files could be in the same directory. (That is the easiest way to make the library known to its importer for this example)
Sample output
On executing the file common_hailstone_length.py it loads the library and produces the following result:
The length of hailstone sequence that is most common for hailstone(n) where 1<=n<100000, is 72. It occurs 1467 times
Note that the file common_hailstone_length.py is itself written as an executable library. When imported it makes functions hailstone_length_frequency as well as hailstone available to the importer.
Ruby
(Ruby 1.9).
<lang ruby># hailstone.rb module Hailstone
def sequence(n) seq = [n] seq << (n = if n.even? then n / 2 else n * 3 + 1 end) until n == 1 seq end module_function :sequence
if __FILE__ == $0 big_hs = Enumerator.new do |y| (1...100_000).each { |n| y << sequence(n) } end.max_by { |hs| hs.size } puts "#{big_hs[0]} has a hailstone sequence length of #{big_hs.size}." puts "The largest number in that sequence is #{big_hs.max}." end
end</lang>
<lang ruby># hsfreq.rb require 'hailstone'
module Hailstone
def most_common_length(enum) h = Hash.new(0) enum.each { |n| h[sequence(n).length] += 1 } h.max_by { |length, count| count } end module_function :most_common_length
if __FILE__ == $0 last = 99_999 length, count = most_common_length(1..last) puts "Given the hailstone sequences for 1 to #{last}," puts "the most common sequence length is #{length}," puts "with #{count} such sequences." end
end</lang>
$ ruby19 hailstone.rb 77031 has a hailstone sequence length of 351. The largest number in that sequence is 21933016. $ ruby19 -I. hsfreq.rb Given the hailstone sequences for 1 to 99999, the most common sequence length is 72, with 1467 such sequences.