Tuesday, August 25, 2009

Discrete Maths, ProblemSet#3

These are my solutions to 3rd problem set of discrete mathematics course taught at Arsdigita university by Shai Simonson. Other posts relating to this are here.

The code for various problems is here...

;P-2c
(define nil '())
;this is a LIFO stack(peg for TOH) in which you can put
;numbers(number represents disk)
;bigger number represents a disk with bigger size
;Each peg/stack has a name also
(define (make-disk-stack stack-name)
(let ((disks nil))
(define (put-disk n)
(if (and (not (null? disks)) (> n (car disks)))
(error "ERROR: trying to put bigger disk
over smaller disk. new-disk: " n
" on stack:" stack-name disks)
(set! disks (cons n disks))))
(define (remove-disk)
(if (null? disks)
(error "Error: No disk is available." stack-name)
(let ((tmp (car disks)))
(set! disks (cdr disks))
tmp)))
(define (size)
(length disks))
(define (print)
(display disks)
(newline))
(define (name)
stack-name)
(define (dispatch msg . args)
(cond
((eq? msg 'put-disk) (put-disk (car args)))
((eq? msg 'remove-disk) (remove-disk))
((eq? msg 'size) (size))
((eq? msg 'print) (print))
((eq? msg 'name) (name))
(else (error "Error: msg not recognized." stack-name))))
dispatch))
(define (put-disk n stack)
(stack 'put-disk n))
(define (remove-disk stack)
(stack 'remove-disk))
(define (size stack)
(stack 'size))
(define (print stack)
(stack 'print))
(define (name stack)
(stack 'name))

;create stack containing n disks
(define (fill-stack stack n)
(if (= n 0) stack
(begin (put-disk n stack)
(fill-stack stack (- n 1)))))

;=====================================
;just to check that my approach is correct, I'm doing
;the standard TOH algo here
(define (Standard-TOH from to using n)
(if (= n 1)
(begin
(display (string-append "moving disk from " (name from)
" to " (name to)))
(newline)
(put-disk (remove-disk from) to))
(begin
(Standard-TOH from using to (- n 1))
(put-disk (remove-disk from) to)
(Standard-TOH using to from (- n 1)))))

;lets do for 7 disks
(define to (make-disk-stack "TO"))
(define from (fill-stack (make-disk-stack "FROM") 7))
(Standard-TOH from to (make-disk-stack "USING") 7)
(print to) ;should print (1 2 3 4 5 6 7)
(print from) ;should print ()


;==================================

;Sloppy Joe's algorithm
(define (TOH from to using1 using2 n)
(if (= n 1)
(begin
(display (string-append
"moving disk from " (name from) " to " (name to)))
(newline)
(put-disk (remove-disk from) to))
(begin
(TOH from using1 to using2 (/ n 2))
(TOH from to using1 using2 (/ n 2))
(TOH using1 to from using2 (/ n 2)))))

(TOH (fill-stack (make-disk-stack "FROM") 4)
(make-disk-stack "TO")
(make-disk-stack "USING1") (make-disk-stack "USING2") 4)
;ERROR: trying to put bigger disk over
;smaller disk. new-disk: 3 " stack:" (1 2)
(TOH (fill-stack (make-disk-stack "FROM") 8)
(make-disk-stack "TO")
(make-disk-stack "USING1") (make-disk-stack "USING2") 8)
;ERROR: trying to put bigger disk over
;smaller disk. new-disk: 3 " stack:" (1 2)

;Fruity Freddie algorithm
(define (TOH from to using1 using2 n)
(if (= n 1)
(begin
(display (string-append
"moving disk from " (name from) " to " (name to)))
(newline)
(put-disk (remove-disk from) to))
(begin
(TOH from using1 to using2 (/ n 2))
(TOH from to using2 using1 (/ n 2))
(TOH using1 to from using2 (/ n 2)))))

(TOH (fill-stack (make-disk-stack "FROM") 4)
(make-disk-stack "TO")
(make-disk-stack "USING1") (make-disk-stack "USING2") 4)
; Finishes successfully
(TOH (fill-stack (make-disk-stack "FROM") 8)
(make-disk-stack "TO")
(make-disk-stack "USING1") (make-disk-stack "USING2") 8)
;ERROR: trying to put bigger disk over
;smaller disk. new-disk: 7 " stack:" (1 2 3 4)


;3 a,b
;steps
;if n = 1 then move 1 disk from From to To
;else
;Move n/2 disks to Using1 from From, using To and Using2
;Move remaining n/2 disks from From to To using Using2 only with
;standard TOH algorithm
;Move n/2 disks from Using1 to To using From and Using2
(define (TOH from to using1 using2 n)
(if (= n 1)
(begin
(display (string-append
"moving disk from " (name from) " to " (name to)))
(newline)
(put-disk (remove-disk from) to))
(begin
(TOH from using1 to using2 (quotient n 2))
(Standard-TOH from to using2 (- n (quotient n 2)))
(TOH using1 to from using2 (quotient n 2)))))


;lets do for 16 disks
(define to (make-disk-stack "TO"))
(define from (fill-stack (make-disk-stack "FROM") 15))
(TOH from to (make-disk-stack "USING1") (make-disk-stack "USING2") 15)
(print to) ;should print (1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)
(print from) ;should print ()


;11a
;Algorithm
;num = (d1 d2 ...dn)
;result = (g1 g2 ...gn)
;start with dn, if dn-1 = 1 then gn=1-dn or gn=dn
;go all the way till d1, d0 is assumed to be 0
;so g1 = d1
(define (bin->gray num)
(define (iter n a)
(if (null? (cdr n))
(cons (car n) a)
(if (= (cadr n) 1)
(iter (cdr n) (cons (- 1 (car n)) a))
(iter (cdr n) (cons (car n) a)))))
(iter (reverse num) nil))

;11b
(define (gray->bin num)
(define (iter n a)
(if (null? n) a
(if
(= (modulo (reduce + 0 (cdr n)) 2) 1)
(iter (cdr n) (cons (- 1 (car n)) a))
(iter (cdr n) (cons (car n) a)))))
(iter (reverse num) nil))

Here are my solutions...





Discrete Maths, Lecture-10

These are my notes from 10th lecture of discrete mathematics course taught at Arsdigita university by Shai Simonson. Other posts relating to this are here.

This lecture covers basic induction with Josephus problem as an example. Here are my notes...


Monday, August 24, 2009

Discrete Maths, Lecture-8,9 [Solving Recurrance relations]

These are my notes from 8th and 9th lecture of discrete mathematics course taught at Arsdigita university by Shai Simonson. Other posts relating to this are here.

These lectures cover the methods of solving recurrence relations, which are...
  • Repeated Substitution
  • Master Method
  • Change of Variables
  • Guess and prove by induction
  • Linear homogeneous/non-homogeneous recurrence solving methods
Here are my notes...









Sunday, August 23, 2009

covariance-contravariance and constraining type params

If you define a class saying something like "class Test[A]" in scala then..
  • It is important to notice that Test is not a type but Test[Any] or Test[String] is.
  • At a place where Test[Any] is expected, you can not give Test[String] ... as was my notion due to mostly being a java programmer.
The idea of whether Test[String] should be considered subtype of Test[Any] is called "covariance". Only when you define your class like "class Test[+A]" then Test[String] will be considered subtype of Test[Any] and will be accepted wherever a Test[Any] is needed. '+' indicates that subtyping is covariant(flexible) in that parameter. In java, types are covariant by default.

Contravariance is exactly opposite of covariance and is supported with '-' symbol instead of '+'

Similarly, to constraint type parameters we can say things like...
class MyClass[V <: AnotherType]
This puts the constraint that V has to be a subtype of AnotherType and
class MyClass[V >: AnotherType]
puts the constraint that V has to be a type that is extended by AnotherType

Programming In Scala: Chapter#8-15 notes

Ch8- Functions and Closures
Functions in scala are first class citizens like any other language supporting functional paradigms such as scheme, lisp etc. Which means you can do all the cool abstractions that SICP teaches you.

Function values are objects and hence can be assigned to variables.

*****************************************************************************
"short" forms for writing function values, Let us try to see it through this example..

numbers.filter((x: Int) => x > 0)

We can remove the type parameter types because compiler can infer that from its usage.

numbers.filter((x) => x > 0)

We can remove the parentheses around a parameter whose type is inferred.

numbers.filter(x => x > 0)

You can use '_' as placeholders for one or more parameters as long as each parameter appears *only once* within the function literal. This is also called placeholder syntax.

numbers.filter(_ > 0)

Sometimes placeholder syntax wouldn't work when compiler don't have enough information to infer the missing parameter types. For example val f = _ + _ doesn't compile but val f = (_: Int) + (_: Int) does. Note that first '_' refers to first parameter, second to second parameter and so on.

*****************************************************************************

Partially Applied functions:
Let us see an example..

scala> def sum(a: Int, b: Int, c:Int) = a + b + c
sum: (Int,Int,Int)Int

//Partially apply the method sum on 3,4 , this gives us
//a Partially Applied Function value which takes remaining
//arguments
scala> val a = sum(_: Int, 3,4)
a: (Int) => Int =

//see, we can apply a on one remaining parameter
scala> a(5)
res24: Int = 12

//We can do the partial application also by not supplying
//any param and putting placeholders for all of them
scala> val a = sum(_: Int, _: Int, _: Int)
a: (Int, Int, Int) => Int =

//above scenario can also be written as following. notice '_' in this
//context here does not refer to a single parameter but the whole parameter
//list
scala> val a = sum _
a: (Int, Int, Int) => Int =

scala> a(5,3,4)
res25: Int = 12


If you are writing a partially applied function expression in which you leave off all parameters, such as println _, you can express it more concisely by leaving off the underscore *if function is required at that point* in the code. For example, these three are ok.

numbers.foreach(println _) //note, '_' is not one param but whole param list
numbers.foreach(println)
val a = sum _


but following is not, as its not required at this point in the code.
val a = sum

*****************************************************************************

It supports closures, When a function on calling it returns a function that encloses one or more variable bindings defined in the parent function. These returned functions are called closures(they have bindings closed in them). In general, any function enclosing binding for one or more free variables is a closure. This technique is very powerful and you can do all sorts of data abstractions using them. For a primer on this one should read SICP chapter 3.

Repeated Parameters: def echo(args:String*) means that you can pass 0 or more strings when calling echo and they will be available in the echo definition in an Array[String] named args.

Due to limitation of jvm instruction set, tail-recursion is optimized only for direct tail recursion with the same function. Other indirect tail recursion cases such as mutually recursive functions, it is not optimized.

Ch9- Control Abstraction
Currying: A curried function is applied to multiple argument list instead of one. When you apply a curried function, you actually get multiple(equal to the number of argument lists it has) regular function invocations back to back.
Exp:
def curriedSum(x:Int)(y:Int) = x + y
curriedSum(1)(2) = 3
Basically curriedSum(1) returns a function that is (y:Int)=>1+y and this function is applied on 2.

In any method invocation in scala in which you're passing in exactly one argument, you can opt to use curly braces to surround the argument instead of parentheses. This is provided, so that you could define control abstractions as functions and they can be called with argument in curly braces which looks more like built-in language construct. Though powerful, but I would rather simply use parentheses so that anyone reading the code *knows* without a doubt that a function is being called and this is *not* a built-in construct.

By-Name Parameter:
Let us say you want to pass an expression to a function, that you don't want to be evaluated at call time but rather inside the body of the function. You can do something like following(basically take a empty param function value as argument)..
def myMethod(proc: () => Boolean)

and the client code would look like..
myMethod(() => 5>3)

Instead you can use By-name params which start with '=>'. Here is how it'll look then..
def myMethod(proc: => Boolean)

and the client code would look like..
myMethod(5>3)
Note that, the expression (5 <3) will not be executed unless myMethod uses proc somewhere inside the body.

Ch10- Composition and Inheritance
For abstract methods, one does not need to specify the "abstract" keyword as that in java

override keyword is mandatory if you're overriding a method from the parent class, its optional if you're implementing an abstract method of the parent class.

Scala has same namespace for instance variable and methods names unlike java. That is you can't have a field and a method with same identifier in scala AND a field(val) can override a parameterless method(def) with same name. However, viceversa is not true that is a parameterless method can't override a field with same name.

In general, scala's inheritence and composition is pretty similar to java's. But there are some striking differences in the syntax and it offers a bag of tricks to reduce code size.

One of the important things to learn from this chapter is the design it offers in the end for exposing the abstract Element via factory methods without exposing any of the concrete subtypes of Element as follow...

abstract class Element {
...
...
private class ArrayElement(x: Array[String]) extends Element { ..}
private class LineElement(s: String) extends Element { ... }
}
object Element {
def elem(x: Array[String]) = new ArrayElement(x)
def elem(x: String) = new LineElement(x)
}


Ch11- Scala's Hierarchy
AnyRef is just an alias for java.lang.Object. By default, all the scala classes inherit AnyRef and a marker trait called ScalaObject.

Instead of ==, In scala AnyRef has a method eq that is final and does the reference equality check. Opposite of eq is ne.

scala.Null is subclass of every reference(not value) class and scala.Nothing is subclasses of every class in scala.

Ch12- Traits
Traits are fundamental unit of code reuse in scala, They contain method and field definitions, which can then be reused by mixing them into classes. One class can mixin with any number of traits.

Traits are defined exactly like class except the keyword use is trait instead of class. It can be mixed in to a class using either the extends or with keywords. If you use extends then superclass of the trait is automatically inherited.

A trait defines a type also. So you can have var or val whose type is a trait's name.

Traits can't have any parameters in its constructor.

In traits, super calls are dynamically bound, while in normal classes its statically bound.

Two major use of traits are...
> turning a thin interface into a rich one (by providing implementation of some of the methods)
> use them as stackable modifications: see this post.

To trait, or not to:
If the behavior will not be reused, then make it a concrete class. If the behavior will be reused in multiple unrelated classes then make it a trait. Use abstract class instead if you want to inherit it from java code, since there is no analogue to traits in java.
And BTW, a trait with only abstract members translates directly into a java interface.

Ch13 - Packages and Imports
In Scala, access modifier can be augmented with qualifiers. A modifier of the form private[X] or protected[X] means that access is private or protected "up to" X, where X designates some enclosing package, class or singleton object.

Other interesting thing about imports in scala are that they can appear anywhere, can refer to objects and also naming the imports.. some examples of import definitions would like following...

import search._ //imports everything inside package search
import search.DepthFirstSearch //imports definition of DepthFirstSearch
import search.{DepthFirstSearch => DFS} //importing same as above, but can refer as DFS.. renamed the import

Ch15 - Case Classes
They're used to provide pattern matching(a very general one).

Scala compiler adds some convenience methods to a case class-
  • You can construct the instance without writing the new keyword.
  • All the paratemers defined in the primary class constructor automatically get the val prefix, so that they are visible as fields automatically.
  • The compiler adds "natural" implementaions of hashCode(), toString() and equals() to your class.

match in scala vs switch in java -
  • match is an expression(always returns a value)
  • alternative expressions never "fall through" into the next case, no need for break.
  • if none of the patterns match then it throws MatchError exception

Mostly all patterns look exactly like the corresponding expression.

Type of Patterns:
  • Wildcard pattern (_): matches anything
  • Constant pattern : matches constants to themselves
  • Variable pattern : a variable in the pattern matches to any object and scala binds the variable to the matched object
  • Scala treats all the identifiers starting with a capital letters as constant, same is really a constant expression and not a variable expression. If you want to use an identifies starting with a small letter as constant expression then you'll have to enclose it in backquotes.
  • Constructor pattern: Assuming A is a case class we can use the constructor expression of this class as a pattern.
  • Sequence patterns : You can match against scala sequences like List or Array
  • Tupple patterns : You can match against tuples too
  • Typed patterns: They can be used as a convenient replacement for the type tests, such as *case x:String => whatver* .. this is something similar to x.isInstanceOf[String]. (Note: to cast something into string you would write x.asInstanceOf[String], as you can see they're rather verbose and that is because scala discourages their use, you should better use typed pattern)
  • Scala uses the erasure model of generics just like java, that means no information about the type argument is maintained at runtime, that is with the pattern matching you can match an object for being Map but not Map[Int, Int].
  • Variable binding pattern: with this you can bind the result of a pattern match to a variable. For example, the pattern *UnOp("abs", e @ UnOp("abs", _))* matching will bind e to result of pattern appearing after @.

Pattern guards:
In general, scala patterns are linear that is you can't write same variable twice in a pattern, if you need such thing anywhere.. look up pattern guards :)
Patterns are tried in the order they are written.

Sealed Class:
If a class is sealed, then only classes defined in the same file can inherit it. This lets scala help you fulfil your intent of not letting someone else define another case class inheriting it.

Option:
The best way to get value apart from optional value(Some(value)) is by using a match expression. It recommeded to use Optional values instead of nulls in scala.

Patterns Everywhere:
Patterns are allowed in many more places and not just in match expression.
  • Whenever you define a val or var, you can use pattern instead of simple identifier.
  • A sequence of cases in curly braces can be used anywhere a function value can be used.
  • If you're using a sequence of cases as function literal and you don't want to exhaust all the cases then make it a PartialFunction. Partial functions have a method "isDefinedAt" that can tell whether its defined for a particular input or not. Partial functions are recommended not be used unless there is a real good reason to use them.
  • Patterns can be used in for expressions also.

Saturday, August 22, 2009

Discrete Maths, Lecture-7

These are my notes from 7th lecture of discrete mathematics course taught at Arsdigita university by Shai Simonson. Other posts relating to this are here.

In this lecture, we solve the chinese ring puzzle. This lecture is not just about presenting a solution to the mentioned problem but the thing one learns here is how you can approach such problems. Here is what we do in this lecture...

  1. Model the problem in mathematical terms.

  2. Come up with the solution algorithm by thinking recursively(that is, by thinking like, can we solve for n rings if we had solution for n-1 rings)

  3. Creating recurrence relation for the complexity from the algorithm

  4. Notice that we can not solve it with *repeated substitution technique*(though there are other techniques, but only this technique has been covered till now), so we try to make a guess by looking at T(1), T(2), T(3).....

  5. We form a conjecture based on the pattern and then prove the conjecture with induction

  6. Then we reduce the original complex recurrence relation into a very simple recurrence relation that is solvable easily with repeated substitution techniue.

  7. We make other important observations like the pattern in the binary representation of T(1), T(2)... that immediately fits a pattern in the first look, so the moral is that one can try looking at alternate representations of the same thing to get different interesting perpectives... explore!

  8. Then we make a note that the sequence generated by chinese ring configurations is also a gray code sequence that is the next configuration in the sequence differs from previous only by one bit.


Here are my notes...



Discrete Maths, Lecture-6

These are my notes from 6th lecture of discrete mathematics course taught at Arsdigita university by Shai Simonson. Other posts relating to this are here.

In this lecture we're introduced to recursion and recurrence relations which arise in analysis of recursive algorithms, "repeated substitution"... the very basic technique of solving recurrence relations is covered with tower of hanoi puzzle as an example.

Here are my notes....