Fractran: Difference between revisions

From Rosetta Code
Content added Content deleted
(initial haskell implementation)
Line 103: Line 103:
{{out}}
{{out}}
<pre>[2, 15, 825, 725, 1925, 2275, 425, 390, 330, 290, 770, 910, 170, 156, 132]</pre>
<pre>[2, 15, 825, 725, 1925, 2275, 425, 390, 330, 290, 770, 910, 170, 156, 132]</pre>

=={{header|Haskell}}==
<lang haskell>import Data.List (find)
import Data.Ratio (Ratio, (%), denominator)

fractran :: (Integral a) => [Ratio a] -> a -> [a]
fractran fracts n = n :
case find (\f -> n `mod` denominator f == 0) fracts of
Nothing -> []
Just f -> fractran fracts $ truncate (fromIntegral n * f)

main :: IO ()
main = print $ take 15 $ fractran [17%91, 78%85, 19%51, 23%38, 29%33, 77%29,
95%23, 77%19, 1%17, 11%13, 13%11, 15%14, 15%2, 55%1] 2</lang>

{{out}}
<pre>[2,15,825,725,1925,2275,425,390,330,290,770,910,170,156,132]</pre>


=={{header|Java}}==
=={{header|Java}}==

Revision as of 08:18, 22 January 2014

Task
Fractran
You are encouraged to solve this task according to the task description, using any language you may know.

FRACTRAN is a Turing-complete esoteric programming language invented by the mathematician John Horton Conway.

A FRACTRAN program is an ordered list of positive fractions , together with an initial positive integer input .

The program is run by updating the integer as follows:

  • for the first fraction, , in the list for which is an integer, replace by  ;
  • repeat this rule until no fraction in the list produces an integer when multiplied by , then halt.

Conway gave a program for primes in FRACTRAN:

, , , , , , , , , , , , ,

Starting with , this FRACTRAN program will change in , then , generating the following sequence of integers:

, , , , , , , , , , ,

After 2, this sequence contains the following powers of 2:

, , , , , , , ,

which are the prime powers of 2.

More on how to program FRACTRAN as a universal programming language will be find in the references.

Your task is to write a program that reads a list of fractions in a natural format from the keyboard or from a string, to parse it into a sequence of fractions (i.e. two integers), and runs the FRACTRAN starting from a provided integer, writing the result at each step. It a also required that the number of step is limited (by a parameter easy to find).

References
  • J. H. Conway (1987). Fractran: A Simple Universal Programming Language for Arithmetic. In: Open Problems in Communication and Computation, pages 4–26. Springer.
  • J. H. Conway (2010). "FRACTRAN: A simple universal programming language for arithmetic". In Jeffrey C. Lagarias. The Ultimate Challenge: the 3x+1 problem. American Mathematical Society. pp. 249–264. ISBN 978-0-8218-4940-8. Zbl 1216.68068.

D

Simple Version

Translation of: Java

<lang d>import std.stdio, std.algorithm, std.conv, std.array;

void fractran(in string prog, int val, in uint limit) {

   const fracts = prog.split.map!(p => p.split("/").to!(int[])).array;
   foreach (immutable n; 0 .. limit) {
       writeln(n, ": ", val);
       const found = fracts.find!(p => val % p[1] == 0);
       if (found.empty)
           break;
       val = found.front[0] * val / found.front[1];
   }

}

void main() {

   fractran("17/91 78/85 19/51 23/38 29/33 77/29 95/23
             77/19 1/17 11/13 13/11 15/14 15/2 55/1", 2, 15);

}</lang>

Output:
0: 2
1: 15
2: 825
3: 725
4: 1925
5: 2275
6: 425
7: 390
8: 330
9: 290
10: 770
11: 910
12: 170
13: 156
14: 132

Lazy Version

<lang d>import std.stdio, std.algorithm, std.conv, std.array, std.range;

struct Fractran {

   int front;
   bool empty = false;
   const int[][] fracts;
   this(in string prog, in int val) {
       this.front = val;
       fracts = prog.split.map!(p => p.split("/").to!(int[])).array;
   }
   void popFront() {
       const found = fracts.find!(p => front % p[1] == 0);
       if (found.empty)
           empty = true;
       else
           front = found.front[0] * front / found.front[1];
   }

}

void main() {

   Fractran("17/91 78/85 19/51 23/38 29/33 77/29 95/23
             77/19 1/17 11/13 13/11 15/14 15/2 55/1", 2)
   .take(15).writeln;

}</lang>

Output:
[2, 15, 825, 725, 1925, 2275, 425, 390, 330, 290, 770, 910, 170, 156, 132]

Haskell

<lang haskell>import Data.List (find) import Data.Ratio (Ratio, (%), denominator)

fractran :: (Integral a) => [Ratio a] -> a -> [a] fractran fracts n = n :

 case find (\f -> n `mod` denominator f == 0) fracts of
   Nothing -> []
   Just f -> fractran fracts $ truncate (fromIntegral n * f)

main :: IO () main = print $ take 15 $ fractran [17%91, 78%85, 19%51, 23%38, 29%33, 77%29,

        95%23, 77%19, 1%17, 11%13, 13%11, 15%14, 15%2, 55%1] 2</lang>
Output:
[2,15,825,725,1925,2275,425,390,330,290,770,910,170,156,132]

Java

<lang java>import java.util.Vector; import java.util.regex.Matcher; import java.util.regex.Pattern;

public class Fractran{

  public static void main(String []args){ 
      new Fractran("17/91 78/85 19/51 23/38 29/33 77/29 95/23 77/19 1/17 11/13 13/11 15/14 15/2 55/1", 2);
  }
  final int limit = 15;
  
  Vector<Integer> num = new Vector<>(); 
  Vector<Integer> den = new Vector<>(); 
  public Fractran(String prog, Integer val){
     compile(prog);
     dump();
     exec(2);
   }


  void compile(String prog){
     Pattern regexp = Pattern.compile("\\s*(\\d*)\\s*\\/\\s*(\\d*)\\s*(.*)");
     Matcher matcher = regexp.matcher(prog);
     while(matcher.find()){
        num.add(Integer.parseInt(matcher.group(1)));
        den.add(Integer.parseInt(matcher.group(2)));
        matcher = regexp.matcher(matcher.group(3));
     }
  }
  void exec(Integer val){
      int n = 0;
      while(val != null && n<limit){
          System.out.println(n+": "+val);
          val = step(val);
          n++;
      }
  }
  Integer step(int val){
      int i=0; 
      while(i<den.size() && val%den.get(i) != 0) i++;
      if(i<den.size())
          return num.get(i)*val/den.get(i);
      return null;
  }
  void dump(){
      for(int i=0; i<den.size(); i++)
          System.out.print(num.get(i)+"/"+den.get(i)+" ");
      System.out.println();
  }

}</lang>

JavaScript

<lang javascript> var num = new Array(); var den = new Array(); var val ;

function compile(prog){

 var regex = /\s*(\d*)\s*\/\s*(\d*)\s*(.*)/m;
 while(regex.test(prog)){
   num.push(regex.exec(prog)[1]);
   den.push(regex.exec(prog)[2]);
   prog = regex.exec(prog)[3];
 }

}

function dump(prog){

 for(var i=0; i<num.length; i++)
   document.body.innerHTML += num[i]+"/"+den[i]+" ";
 document.body.innerHTML += "
";

}

function step(val){

 var i=0;
 while(i<den.length && val%den[i] != 0) i++;
 return num[i]*val/den[i];

}

function exec(val){

 var i = 0;
 while(val && i<limit){
   document.body.innerHTML += i+": "+val+"
"; val = step(val); i ++; }

}

// Main compile("17/91 78/85 19/51 23/38 29/33 77/29 95/23 77/19 1/17 11/13 13/11 15/14 15/2 55/1"); dump(); var limit = 15; exec(2); </lang>

Perl

Instead of printing all steps, I chose to only print those steps which were a power of two. This makes the fact that it's a prime-number-generating program much clearer.

<lang perl>use strict; use warnings; use Math::BigRat;

my ($n, @P) = map Math::BigRat->new($_), qw{ 2 17/91 78/85 19/51 23/38 29/33 77/29 95/23 77/19 1/17 11/13 13/11 15/14 15/2 55/1 };

$|=1; MAIN: for( 1 .. 5000 ) { print " " if $_ > 1; my ($pow, $rest) = (0, $n->copy); until( $rest->is_odd ) { ++$pow; $rest->bdiv(2); } if( $rest->is_one ) { print "2**$pow"; } else { #print $n; } for my $f_i (@P) { my $nf_i = $n * $f_i; next unless $nf_i->is_int; $n = $nf_i; next MAIN; } last; }

print "\n"; </lang>

If you uncomment the

#print $n

, it will print all the steps.

Perl 6

A FRACTRAN program potentially returns an infinite list, and infinite lists are a common data structure in Perl 6. Thus we won't try to enforce a limit to the number of steps.

Notice that this code will only work with a fairly recent version of rakudo, for it requires the .narrow method, which was added in early 2014.

<lang perl6>sub fractran(@r) {

   sub ($n) { Failure R// first Int, map (* *$n).narrow, @r }

}

say .[^20] given 2, fractran(<

   17/91 78/85 19/51 23/38 29/33 77/29 95/23 77/19 1/17 11/13 13/11 15/14 15/2 55/1

>) ...^ Failure;</lang>

Output:
2 15 825 725 1925 2275 425 390 330 290 770 910 170 156 132 116 308 364 68 4

Python

<lang python>from fractions import Fraction

def fractran(n, fstring='17 / 91, 78 / 85, 19 / 51, 23 / 38, 29 / 33,'

                       '77 / 29, 95 / 23, 77 / 19, 1 / 17, 11 / 13,'
                       '13 / 11, 15 / 14, 15 / 2, 55 / 1'):
   flist = [Fraction(f) for f in fstring.replace(' ', ).split(',')]
   yield n
   while True:
       for f in flist:
           if (n * f).denominator == 1:
               break
       else:
           break
       n *= f
       yield n.numerator
   

if __name__ == '__main__':

   n, m = 2, 15
   print('First %i members of fractran(%i):\n  ' % (m, n) +
         ', '.join(str(f) for f,i in zip(fractran(n), range(m))))</lang>
Output:
First 15 members of fractran(2):
  2, 15, 825, 725, 1925, 2275, 425, 390, 330, 290, 770, 910, 170, 156, 132

REXX

Programming note:   extra blanks can be inserted in the fractions before and/or after the solidus   [/]. <lang rexx>/*REXX pgm runs FRACTAN for a given set of fractions and from a given N.*/ numeric digits 100 /*be able to handle larger nums. */ parse arg N terms fracs /*get optional arguments from CL.*/ if N== | N==',' then N=2 /*N specified? No, use default.*/ if fracs= then fracs= , /*any fractions specified? No···*/ '17/91, 78/85, 19/51, 23/38, 29/33, 77/29, 95/23, 77/19, 1/17, 11/13, 13/11, 15/14, 15/2, 55/1' f=fracs /* [↑] use default for fractions.*/

       do i=1  while f\==;   parse var f @.i ',' f
       end   /*i*/                    /* [↑]   parse all the fractions.*/
  1. =i-1 /*the number of fractions found. */

say # 'fractions:' fracs /*display # and actual fractions.*/ say 'N is starting at:' N /*display the starting number N.*/ if terms== | terms==',' then terms=100 /*¬ specified? Use default.*/ say terms 'terms being shown:' /*display a kind of header/title.*/ say /*show blank line (perusability).*/

      do j=1  for  terms              /*perform loop once for each term*/
         do k=1  for  #;  interpret '_=' N "*" @.k    /* [↓]  integer? */
         if \datatype(_,'W')  then iterate    /*Not integer?   Skip it.*/
         say right(j||th(j),15) 'term: '  N   /*display formatted term.*/
         N=_                          /*set the next  N  to be used.   */
         leave                        /*go start calculating next term.*/
         end   /*k*/                  /* [↑]  if integer, found a new N*/
      end      /*j*/

exit /*stick a fork in it, we're done.*/ /*──────────────────────────────────TH subroutine───────────────────────*/ th: procedure;parse arg x;x=abs(x);return word('th st nd rd',1+x//10*(x//100%10\==1)*(x//10<4))</lang> output using the default input:

14 fractions: 17/91, 78/85, 19/51, 23/38, 29/33, 77/29, 95/23, 77/19, 1/17, 11/13, 13/11, 15/14, 15/2, 55/1
N is starting at: 2
100 terms being shown:

            1st term:  2
            2nd term:  15
            3rd term:  825
            4th term:  725
            5th term:  1925
            6th term:  2275
            7th term:  425
            8th term:  390
            9th term:  330
           10th term:  290
           11th term:  770
           12th term:  910
           13th term:  170
           14th term:  156
           15th term:  132
           16th term:  116
           17th term:  308
           18th term:  364
           19th term:  68
           20th term:  4
           21st term:  30
           22nd term:  225
           23rd term:  12375
           24th term:  10875
           25th term:  28875
           26th term:  25375
           27th term:  67375
           28th term:  79625
           29th term:  14875
           30th term:  13650
           31st term:  2550
           32nd term:  2340
           33rd term:  1980
           34th term:  1740
           35th term:  4620
           36th term:  4060
           37th term:  10780
           38th term:  12740
           39th term:  2380
           40th term:  2184
           41st term:  408
           42nd term:  152
           43rd term:  92
           44th term:  380
           45th term:  230
           46th term:  950
           47th term:  575
           48th term:  2375
           49th term:  9625
           50th term:  11375
           51st term:  2125
           52nd term:  1950
           53rd term:  1650
           54th term:  1450
           55th term:  3850
           56th term:  4550
           57th term:  850
           58th term:  780
           59th term:  660
           60th term:  580
           61st term:  1540
           62nd term:  1820
           63rd term:  340
           64th term:  312
           65th term:  264
           66th term:  232
           67th term:  616
           68th term:  728
           69th term:  136
           70th term:  8
           71st term:  60
           72nd term:  450
           73rd term:  3375
           74th term:  185625
           75th term:  163125
           76th term:  433125
           77th term:  380625
           78th term:  1010625
           79th term:  888125
           80th term:  2358125
           81st term:  2786875
           82nd term:  520625
           83rd term:  477750
           84th term:  89250
           85th term:  81900
           86th term:  15300
           87th term:  14040
           88th term:  11880
           89th term:  10440
           90th term:  27720
           91st term:  24360
           92nd term:  64680
           93rd term:  56840
           94th term:  150920
           95th term:  178360
           96th term:  33320
           97th term:  30576
           98th term:  5712
           99th term:  2128
          100th term:  1288