RosettaCode

_images/unicon.png

Index Unicon

Unicon on rosettacode.org

Along with the IPL, rosettacode.org contains another treasure trove of Unicon programming examples, tips, and learning materials.

http://rosettacode.org

http://rosettacode.org/wiki/Category:Unicon

Everything from simple Hello, world examples to complex graphing and other meaty topics.


Some samples

Combinations and Permutations

A nice example of concise Unicon, and the benefits of large integer support.

http://rosettacode.org/wiki/Combinations_and_permutations#Icon_and_Unicon

procedure C(n,k)
    every (d:=1) *:= 2 to k
    return P(n,k)/d
end

procedure P(n,k)
    every (p:=1) *:= (n-k+1) to n
    return p
end

Where:

write("P(1000,10) = ",P(1000,10))
write("P(1000,15) = ",P(1000,15))

Gives:

P(1000,10) = 955860613004397508326213120000
P(1000,15) = 899864387800469514043972248293019354931200000

Two Sum

Not yet a Unicon guru, here is a possibly less than stellar RosettaCode entry by this author.

http://rosettacode.org/wiki/Two_Sum

#
# twosum.icn, find two array elements that add up to a given sum
# Dedicated to the public domain
#
link fullimag
procedure main(arglist)
    sum := pop(arglist) | 21
    L := []
    if *arglist > 0 then every put(L, integer(!arglist)) & L := sort(L)
    else L := [0, 2, 11, 19, 90]

    write(sum)
    write(fullimage(L))
    write(fullimage(twosum(sum, L)))
end

# assume sorted list, only interested in zero or one solution
procedure twosum(sum, L)
    i := 1
    j := *L
    while i < j do {
        try := L[i] + L[j]
        if try = sum then return [i,j]
        else
            if try < sum then
                i +:= 1
            else
                j -:= 1
    }
    return []
end

examples/onesum.icn

Sample run:

prompt$ unicon -s onesum.icn -x
21
[0,2,11,19,90]
[2,4]

The above sample does not quite highlight the expressive power of Unicon, the task requires zero or one solution. Unicon can do much better than that.

#
# twosum.icn, find two array elements that add up to a given sum
# a modification of http://rosettacode.org/wiki/Two_Sum
#
link fullimag
procedure main(arglist)
    # predictable results for the UP docset
    &random := 1

    # target sum
    sum := pop(arglist) | ?100

    # create a list of the rest of the arguments, or make it up
    L := []
    if *arglist > 0 then every put(L, integer(!arglist))
    else every 1 to 20 do put(L, ?100)
    writes(sum, ", ")
    write(fullimage(L))

    # keep track of result count
    results := 0
    every write(fullimage(pair := twosum(sum, L)), " ",
        L[pair[1]], " ", L[pair[2]]) do results +:= 1
    if results = 0 then write("[], no solution")
end

# O(n^2), nested iterations
procedure twosum(sum, L)
    every i := 1 to *L-1 do
        every j := i+1 to *L do
            if L[i] + L[j] = sum then suspend [i,j]
end

examples/twosum.icn

Sample runs:

prompt$ unicon -s twosum.icn -x
73, [94,32,38,27,97,30,37,18,59,85,91,64,70,92,46,52,9,16,56,56]
[4,15] 27 46
[12,17] 64 9
prompt$ unicon -s twosum.icn -x 10 0 1 2 3 4 5 6 7 8 9 10
10, [0,1,2,3,4,5,6,7,8,9,10]
[1,11] 0 10
[2,10] 1 9
[3,9] 2 8
[4,8] 3 7
[5,7] 4 6

Pathological floating point problems

http://rosettacode.org/wiki/Pathological_floating_point_problems

Found this one interesting, and was initially disappointed (but not for long), as the Unicon real data type falls into the trap of not having enough precision to produce correct results for this task.

The initial quick trial for the sequence convergence shows the problem:

#
# patho.icn, Pathological problems with floating point
#   From RosettaCode
#
link printf
procedure main()
    iterations := [3,4,5,6,7,8,20,30,50,100,200]
    every n := !iterations do {
        ans := patho(n, 2.0, -4.0)
        printf("%3d %19.15r\n", n, ans)
    }
end

# this sequence should converge on 6.0
procedure patho(n, a, v)
    if n < 3 then return v
    return patho(n -:= 1, v, 111-1130/v+3000/(v*a))
end

examples/patho-real.icn

The sample run starts miscalculating the proper convergence pretty much after the first iteration, and then shortly fails catastrophically converging on 100, instead of 6.

prompt$ unicon -s patho-real.icn -x
  3  18.500000000000000
  4   9.378378378378379
  5   7.801152737752169
  6   7.154414480975333
  7   6.806784736924811
  8   6.592632768721792
 20  98.349503122165360
 30  99.999999999998930
 50 100.000000000000000
100 100.000000000000000
200 100.000000000000000

I mentioned it on the SourceForge forum, and how a REXX solution with inherent decimal arithmetic (and explicit control on how many digits are used in calculations) made for a very easy to read and correct solution. Bruce Rennie came to the rescue almost immediately.

Unicon supports infinite length integers, which can be scaled out to allow very precise control over the number of digits used in computations. A little bit of fixed point decimal arithmetic, and large integers make this problem a cake walk. Runs fast and accurate, and the source code is a very clean read.

Bruce posted a small example, which was modified a bit to suit a RosettaCode entry (and to fill out other parts of the task, to further demonstrate the ease of using highly precise fixed point decimal math in Unicon programs).

#
# Pathological floating point problems
#
procedure main()
    sequence()
    chaotic()
end

#
# First task, sequence convergence
#
link printf
procedure sequence()
     local l := [2, -4]
     local iters := [3, 4, 5, 6, 7, 8, 20, 30, 50, 100, 200]
     local i, j, k
     local n := 1

     write("Sequence convergence")
     # Demonstrate the convergence problem with various precision values
     every k := (100 | 300) do {
         n := 10^k
         write("\n", k, " digits of intermediate precision")

         # numbers are scaled up using large integer powers of 10
         every i := !iters do {
             l := [2 * n, -4 * n]
             printf("i: %3d", i)

             every j := 3 to i do {
                 # build out a list of intermediate passes
                 # order of scaling operations matters
                 put(l, 111 * n - (1130 * n * n / l[j - 1]) + 
                        (3000 * n * n * n / (l[j - 1] * l[j - 2])))
             }
             # down scale the result to a real
             # some precision may be lost in the final display 
             printf(" %20.16r\n", l[i] * 1.0 / n)
         }
     }
end

#
# Task 2, chaotic bank of Euler
#
procedure chaotic()
    local euler, e, scale, show, y, d

    write("\nChaotic Banking Society of Euler")
    # format the number for listing, string form, way overboard on digits
    euler :=
"2718281828459045235360287471352662497757247093699959574966967627724076630353_
  547594571382178525166427427466391932003059921817413596629043572900334295260_
  595630738132328627943490763233829880753195251019011573834187930702154089149_
  934884167509244761460668082264800168477411853742345442437107539077744992069_
  551702761838606261331384583000752044933826560297606737113200709328709127443_
  747047230696977209310141692836819025515108657463772111252389784425056953696_
  770785449969967946864454905987931636889230098793127736178215424999229576351_
  482208269895193668033182528869398496465105820939239829488793320362509443117_
  301238197068416140397019837679320683282376464804295311802328782509819455815_
  301756717361332069811250996181881593041690351598888519345807273866738589422_
  879228499892086805825749279610484198444363463244968487560233624827041978623_
  209002160990235304369941849146314093431738143640546253152096183690888707016_
  768396424378140592714563549061303107208510383750510115747704171898610687396_
  9655212671546889570350354"

    # precise math with long integers, string form just for pretty listing
    e := integer(euler)

    # 1000 digits after the decimal for scaling intermediates and service fee
    scale := 10^1000

    # initial deposit, e - $1
    d := e - scale

    # show balance with 16 digits 
    show := 10^16
    write("Starting balance:       $", d * show / scale * 1.0 / show, "...")

    # wait 25 years, with only a trivial $1 annual service fee
    every y := 1 to 25 do {
        d := d * y - scale
    }
    
    # show final balance with 4 digits after the decimal (truncation)
    show := 10^4
    write("Balance after ", y, " years: $", d * show / scale * 1.0 / show)
end

examples/patho.icn

A much more satisfying run:

prompt$ unicon -s patho.icn -x
Sequence convergence

100 digits of intermediate precision
i:   3  18.5000000000000000
i:   4   9.3783783783783790
i:   5   7.8011527377521620
i:   6   7.1544144809752490
i:   7   6.8067847369236330
i:   8   6.5926327687044380
i:  20   6.0435521101892680
i:  30   6.0067860930312060
i:  50   6.0001758466271870
i: 100  99.9999999999998400
i: 200 100.0000000000000000

300 digits of intermediate precision
i:   3  18.5000000000000000
i:   4   9.3783783783783790
i:   5   7.8011527377521610
i:   6   7.1544144809752490
i:   7   6.8067847369236320
i:   8   6.5926327687044380
i:  20   6.0435521101892680
i:  30   6.0067860930312060
i:  50   6.0001758466271870
i: 100   6.0000000193194780
i: 200   6.0000000000000000

Chaotic Banking Society of Euler
Starting balance:       $1.718281828459045...
Balance after 25 years: $0.0399

With only a small bit of fixed point management, Unicon is more than capable of very precise fractional mathematics when real native floating point data may not be up to the task. Some care needs to be taken with the order of scaling within the large integer operations, but other than that, this style of programming is fairly straight forward. It shines another bright light on Unicon for use in general purpose programming.

Note

The COBOL 2014 specification calls for 1,000 digits of internal precision for financially sound computations. Unicon can easily meet and exceed that requirement when using large integers in fixed point decimal calculations. At speed.


Index | Previous: Theory | Next: Notes