unofficial mirror of guile-user@gnu.org 
 help / color / mirror / Atom feed
From: Joshua Branson <jbranso@fastmail.com>
To: guile-user@gnu.org
Subject: I wrote a 3+1 guile program, feedback welcome
Date: Wed, 07 Nov 2018 10:14:06 -0500	[thread overview]
Message-ID: <87efbwc43l.fsf@fastmail.com> (raw)

[-- Attachment #1: Type: text/plain, Size: 732 bytes --]


Hello,

I spent a week or two working on the 3+1 problem.  The 3+1 problem is a
fun number theory conjecture.  It states that there exists a procedure
for turning all positive whole integers into 1.  The procedure is:

If the number is even, then divide it by two.
If the number is odd, multiply by 3 and add 1.
repeat.

I decided to write a program to graph the number of iterations it takes
for the set of numbers 2-n to reach 1.  It uses ice-9 getopt-long, so
the usual --help and --version work.  It uses gnuplot, because I
couldn't get guile-charting to work.  If anyone has enough free time to
comment on the state of this program, or any tips about how to make it
better, etc. that would be most helpful.

Thanks,


Joshua


[-- Attachment #2: collatz.scm --]
[-- Type: text/plain, Size: 7807 bytes --]

#!/usr/bin/guile \
-e main -s
!#

;; This is a program that checks to see if a string of numbers has
;; repeatitive numbers...
;;  take any number, if even / 2,  if odd * 3 + 1  repeat.
;; The goal is to plot the number of steps it takes to produce 1 from 1-100
;; gnuplot can plot things via a command line from standard in via
;; gnuplot -

;; To give gnuplot commands directly in the command line, using the "-persist" option so that the plot remains on the screen afterwards:
;; gnuplot -persist -e "set title 'Sine curve'; plot sin(x)"
;; To set user-defined variables a and s prior to executing commands from a file:
;; gnuplot -e "a=2; s='file.png'" input.gpl

;; use these modules to help my process the command line arguments.
(use-modules (ice-9 getopt-long))
;;(use-modules (charting))
(use-modules (oop goops))
;; (use-modules (fibers))
(use-modules (ice-9 textual-ports)) ;;open-output-file

;; take a number
;; if even / 2
;; if odd * 3 + 1
;; repeat
;; You should end at 1

;;(define counter 0)
;; Every time you call counter, it returns the next higher positive integer
;; (counter #:key #t) resets the value to 0
(define counter
  (let ((counter 0))
    (lambda* (#:key reset)
      (if reset
          (set! counter 0)
          (begin
            (set! counter (+ counter 1))
            counter)))))

;; playing with fibers
;; (run-fibers
;;  (spawn-fiber
;;   (lambda ()
;;     (display
;;      (let loop (n 10)
;;        (if (> n 0)
;;            (cons n (cons loop (- n 1)))
;;            '())))
;;     )
;;   (spawn-fiber
;;    (lambda ()
;;      (display
;;       (let loop (n 20)
;;         (if (> n 10)
;;             (cons n (cons loop (- n 1)))
;;             '())))))))


;; implement the collatz (3 + 1) algorithm on a number.
;; Return the number of steps it takes to turn the number into 1
(define (collatz n)
  (when (>= n 1) ;; n must be greater than or equal to 1
    (let ((count (counter)))
      (cond
       ((= n 1)
        (counter #:reset #t)
        (- count 1))
       ((even? n)
        ;; (display (string-append "collatz (/ " (number->string n) " 2)\n"))
        (collatz (/ n 2)))
       ((odd? n)
        ;; (display (string-append  "collatz (- (* " (number->string n) " 3) 1)\n"))
        (collatz (+ (* n 3) 1)))))))

;; return the number with the most iterations required to get to 1
;; within the set 0-n.  return value is '(number number)
(define (collatz-set n)
  ;; most iterative number the number that takes the most iterations
  ;; to get to 1. (cdr most-it-number) is the number
  ;; (car (cdr most-it-number)) is the number of iterations
  (let ([most-it-number '(0 0)]
        [iterations 0])
    (let loop ((number n)) ;; loop through the numbers 0-n
      (when (> number 1)
        (set! iterations (collatz number))
        ;; error checking (display number) (display " has ")
        ;; error checking (display iterations) (display " iterations\n")
        ;; if the current number's iterations was more than the last one
        ;; then set most-it-number to the new number
        (when (> iterations
                 (car (cdr most-it-number)))
          (set! most-it-number (list number iterations)))
        (loop (- number 1))))
    most-it-number))

;; this function will create a gnuplot script to draw a line through a scatter plot
(define (create-plot-script data-file)
  ;; use a let here instead of all the defines
  (letrec ([script-filename "/tmp/plot.dem"]
           [output-script-port (open-output-file script-filename)])

    (put-string output-script-port "set style fill  transparent solid 1 noborder\n")
    (put-string output-script-port "set style circle radius 0.5\n")
    (put-string output-script-port
                (string-append "plot '" data-file "' with circles"))
    (force-output output-script-port)
    (close-port output-script-port)
    script-filename))



;; graphs a scatter plot from the set of numbers 2-n
;; what's wrong here?  and wow functional programming recursively sure makes for consice code!
(define (graph-collatz-set n)
  ;; number-iterations is the list of numbers and their iterations
  ;; ie:  '((2 . 1) (3 . 8) (4 . 2) ...)
  (letrec ([data-filename "/tmp/data.dat"]
           [output-data-port (open-output-file data-filename)]
           [script-filename (create-plot-script data-filename)])
    (let loop ((number 2) (iterations (collatz 2))) ;; loop through the numbers 0-n
      (when (>= n number)
        ;; write to a file
        (put-string output-data-port
                    (string-append (number->string number) "\t" (number->string iterations) "\n"))
        ;;(display (string-append "number is " number " iterations is " iterations "\n"))
        (loop (+ number 1) (collatz (+ number 1)))))
    (force-output output-data-port)
    (close-port output-data-port)
    (system (string-append "gnuplot "  "-p " script-filename)))
  ;;(delete-file data-filename)
  ;;(delete-file script-filename)
  ;; if guile-charting worked
  ;; (make-scatter-plot
  ;;  "3+1 conjecture"
  ;;  (list
  ;;   (append '("numbers")
  ;;           (let loop ((number 2) (iterations (collatz 2))) ;; loop through the numbers 0-n
  ;;             (if (>= n number)
  ;;                 (cons (cons number iterations) (loop (+ number 1) (collatz (+ number 1))))
  ;;                 '())))))
  )


(define (main args)
  ;;the option specification tells getopt-long how
  ;; to parse the command line
  (let* ((option-spec '((version (single-char #\v) (value #f))
                        (help    (single-char #\h) (value #f))
                        (graph   (single-char #\g) (value #t))
                        (number  (single-char #\n) (value #t))
                        ;; a set of numbers
                        (set     (single-char #\s) (value #t))
                        ))
         ;; tell getopt-long to parse the command line and put the
         ;; data in options
         (options (getopt-long args option-spec))
         ;; was  --help or -h used?
         (help-wanted    (option-ref options 'help #f))
         (version-wanted (option-ref options 'version #f))
         ;; fixme.  This How do I know if graph was wanted?
         (graph-wanted   (option-ref options 'graph #f))
         (number-wanted  (option-ref options 'number #f))
         (set-wanted     (option-ref options 'set #f)))
    (if (or number-wanted version-wanted help-wanted set-wanted graph-wanted)
        (cond
         (version-wanted
          (display "collatz version 0.1\n"))
         (help-wanted
          (display "collatz [options]\n")
          (display "")
          (display "-v  --version  Display version\n")
          (display "-h, --help     Display this help info\n")
          (display "-g, --graph    Graph the set of numbers' iterations via gnuplot\n")
          (display "               ie: collatz -g 500\n")
          (display "-n, --number   Returns the number of iterations required to turn this number to 1\n")
          (display "-s, --set      Specify a set of numbers, to which to apply the 3 + 1 formula.\n")
          (display "               ie: collatz -s 500 will return the number which requires the most\n")
          (display "               iterations in the set of numbers 2-500\n")
          )
         (number-wanted
          (display (collatz (string->number number-wanted)))
          (display "\n"))
         (set-wanted
          (let ((result (collatz-set (string->number set-wanted))))
            (display "In the set of 2-")
            (display set-wanted)
            (display " ")
            (display (car result))
            (display " has the most iterations with ")
            (display (car (cdr result)))
            (display " iterations\n")))
         (graph-wanted
          (graph-collatz-set (string->number graph-wanted)))))))

             reply	other threads:[~2018-11-07 15:14 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-11-07 15:14 Joshua Branson [this message]
2018-11-08  9:37 ` I wrote a 3+1 guile program, feedback welcome Jérémy Korwin-Zmijowski

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/guile/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=87efbwc43l.fsf@fastmail.com \
    --to=jbranso@fastmail.com \
    --cc=guile-user@gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).