Executable library

Revision as of 23:41, 14 March 2011 by rosettacode>Paddy3118 (New task - A clarification of Scripted main with Python and Ruby examples.)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

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.

Executable library is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.

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.