Current section
Files
Jump to
Current section
Files
examples/simple-erl-exercises.lfe
;; Copyright (c) 2008-2020 Sean Chalmers
;;
;; Licensed under the Apache License, Version 2.0 (the "License");
;; you may not use this file except in compliance with the License.
;; You may obtain a copy of the License at
;;
;; http://www.apache.org/licenses/LICENSE-2.0
;;
;; Unless required by applicable law or agreed to in writing, software
;; distributed under the License is distributed on an "AS IS" BASIS,
;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;; See the License for the specific language governing permissions and
;; limitations under the License.
;; File : simple-erl-exercises.lfe
;; Author : Sean Chalmers
;; Purpose : LFE Implementation of Erlang introductory exercises.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; [Erlang Exercises] (http://www.erlang.org/course/exercises.html) ;;
;; ;;
;; Completed using Lisp Flavoured Erlang by Sean Chalmers, November 2013 ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; These are the exercise solutions that are contained here: ;;
;; ;;
;; - Entering a program ;;
;; - Simple sequential programs ;;
;; - Simple recursive programs ;;
;; - Interaction between processes, Concurrency ;;
;; ;;
;; I make no assumptions about these being the best way to solve any ;;
;; of these problems and I encourange feedback and alternate solutions. ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defmodule exercises
(export
(convert 1)
(perimeter 1)
(min 1)
(max 1)
(min_max 1)
(min_max2 1)
(start_pong 1)
(start_ring 2)
(start_star 2)))
;; SIMPLE SEQUENTIAL EXERCISES
;; Temperature Conversion Exercise
(defun f2c (c)
(+ 32 (/ (* c 9) 5)))
(defun c2f (f)
(/ (* 5 (- f 32)) 9))
(defun convert
([(tuple 'c temp)] (tuple 'f (f2c temp)))
([(tuple 'f temp)] (tuple 'c (c2f temp))))
;; End of Temperature Conversion Exercise
;; Perimeter calculation by tuple input exercise
(defun perimeter
;; Square
([(tuple 'square side)] (when (is_number side))
(tuple 'square (* 4 side)))
;; Circle
([(tuple 'circle radius)] (when (is_number radius))
(tuple 'circle (math:pow (* (math:pi) radius) 2)))
;; Triangle
([(tuple 'triangle a b c)] (when (is_number a)
(is_number b)
(is_number c))
(tuple 'triangle (+ a b c))))
;; end of perimeter exercise
;; SIMPLE RECURSIVE EXERCISES
;; I know lists:max/1 and lists:min/1 exist, that isn't the point.
(defun min ([(cons x xs)]
(lists:foldl (fun erlang min 2) x xs)))
(defun max ([(cons x xs)]
(lists:foldl (fun erlang max 2) x xs)))
(defun min_max (col)
;; Flavourless min_max/1 implementation.
(tuple (min col) (max col)))
;; Alternative min_max without the little helpers
(defun min_max2 (col)
(let-function [(gief (match-lambda ([f (cons x xs)] (lists:foldl f x xs))))]
;; Create a tuple using our temp function above.
(tuple (gief (fun erlang min 2) col)
(gief (fun erlang max 2) col))))
(defun nom-date
;; Extract the final digits from the year
([(list _ _ a r)] (list a r))
;; These two handle if our month or day value is a single digit.
([(cons a ())] (list #\0 a))
;; Or if it contains two digits
([(list a b)] (list a b)))
(defun swedish_date ()
(lists:foldl ;; I heart fold
(lambda (x acc)
(++ acc (nom-date (integer_to_list x))))
() ;; This is our accumulator
(tuple_to_list (erlang:date))))
(defun create-pids-one-arg (fn arg col)
;; I ended up using this pattern a few times in the next couple of
;; exercises so I pulled it out into it's own function.
(lists:map (lambda (_) (spawn (MODULE) fn (list arg))) col))
(defun pong (n)
;; This is the pong receiver.
(receive
;; We've reached the maximum number of passes, so pass it on
;; and drop out gracefully.
((tuple _ from count) (when (=:= count n))
(! from (tuple 'ping (self) 10))
'ok)
;; We've received a message, bump the counter and send it back.
((tuple 'ping from count)
(io:format "caught ball~n" (list))
(! from (tuple 'ping (self) (+ count 1)))
;; Make sure we're still here to get the next message.
(pong n))))
(defun start_pong (n)
;; Creates our players.
(let [((list a b) (create-pids-one-arg 'pong n '(1 2)))]
;; Starts the game and exits because the initiator is not part of play.
(! a (tuple 'ping b 1))))
(defun start_ring (n-rings n-msgs)
;; Create the desired number of "servers" in the ring.
(let [((cons x xs)
(create-pids-one-arg 'ring n-msgs (lists:seq 1 n-rings)))]
;; Get it rolling.
(! x (tuple 'pass (++ xs (list x)) 0))))
(defun ring (n-msgs)
;; We need a tiny helper function to just ease the rebuild of the ring list.
;; I'm sure there is a more efficient/idiomatic method of doing this...
(let-function [(ring-col (lambda (x xs) (++ xs (list x))))]
(receive
;; We've reached the maxiumum number of messages
((tuple 'pass (cons x xs) msg) (when (== msg n-msgs))
;; State our intentions.
(io:format "Shutting Down.~n" '())
;; Ensure we trigger the shut down of all remaining rings.
(! x (tuple 'pass (ring-col x xs) msg)))
;; We've received a message, pass it on to the next ring.
((tuple 'pass (cons x xs) msg)
(io:format "Recieved Message~n" '())
(! x (tuple 'pass (ring-col x xs) (+ msg 1)))
;; Make sure we're still around to receive the next one.
(ring n-msgs)))))
(defun contact_stars
;; This function sends a message to each of the individual stars in turn
;; until the list is exhausted.
([()] 'done) ;; No more stars, we're done here.
([(cons x xs)]
(io:format "Sent message to star~n" '())
(! x (tuple 'msg (self)))
(receive ;; Wait until the star replies before moving on to the next one.
;; Recur into the rest of the list.
('ok (contact_stars xs)))))
(defun start_star ;; Start our star communication process
((n-stars n-msgs) (when (is_number n-stars) (is_number n-msgs))
;; Use the function from earlier to create our list of pids
(let* [(stars (create-pids-one-arg 'star n-stars (lists:seq 1 n-stars)))
;; For every message, trigger a sequence of communication with every
;; star. This is inside the let* so I can deliberately discard the
;; value and not be yelled at by the compiler.
(_ (lc ((<- _ (lists:seq 1 n-msgs))) (contact_stars stars)))]
;; Ensure all the star processes are killed off.
(lc ((<- star stars)) (! star 'die)))))
(defun star (x) ;; Our star receiver function
(receive
((tuple 'msg from) ;; Received a message from the core.
(io:format "Received msg from center~n" '())
(! from 'ok)
(star x))
('die 'ok))) ;; Received instruction to die from the core.