F#ing brilliant

I think there’s a time for all of us when we sat down to write a piece of code and it worked perfectly first time, and we thought to ourselves that perhaps we might just make developers of ourselves yet – shortly before we found an edge case that wasn’t covered, or irreparably broke everything with our overconfidence.

I’ve recently been going through Project Euler problems again – having solved a great many of them in C# – and solving them in F#. And it’s been amazing. That feeling of things working first time has become more commonplace than it has any right to be, particularly in a language that I’m (relatively) unfamiliar with and when I’m writing code with no IDE niceties (LinqPad); and since I’m fairly certain that I haven’t become insanely competent at overnight, I can’t help but feel that it can only be attributed to the language and paradigm itself.

If I wasn’t before, I’m certainly feeling the love for F# now.

For example…

Problem 13 is incredibly simple if you have an arbitrary-sized integer type, and while I could easily use Math.bigint, I thought it might be a nice exercise to create my own. Now, the code below may not be the prettiest in the world, and it certainly isn’t the most efficient, but for something that I knocked together in a few minutes in my spare time without taking too much care over… it worked first time! (Well… almost anyway… the first time around I forgot the case where the digits had all been dealt with but carried wasn’t zero.) Hopefully that doesn’t undermine my point too much, because I’ve truly been impressed by how much I feel helped rather than hindered by the language.

type BigNum (digits: int list) = 
	// digits: list of decimal digits smallest-biggest
	
	static let carryDigits digits = 
		let rec carryDigits digits processed carried = 
			let getDigitAndCarry next = 
				next % 10, next / 10
			match digits with
			| [] -> 
				match carried with
				| 0 -> processed // all done and nothing more to carry
				| _ -> // digits all done, but still carrying left
					let digit, carriedNext = getDigitAndCarry carried
					carryDigits digits (digit :: processed) carriedNext
			| _ -> // keep going...!
				let next = (List.head digits) + carried
				let digit, carriedNext = getDigitAndCarry next
				carryDigits (List.tail digits) (digit :: processed) carriedNext
		carryDigits digits [] 0 |> List.rev
	
	member this.digits = carryDigits digits
	
	static member Create (s: string) =
		let numStringToList (s: string) = 
			s
			|> Seq.map (string >> Int32.Parse)
			|> List.ofSeq
			|> List.rev
		let digits = numStringToList s
		BigNum(digits)
	
	static member (+) (a: BigNum, b: BigNum) =
		let matchLengths list1 list2 = 
			let extend list toLength = 
				let lengthToAdd = max 0 (toLength - List.length list)
				let zeroes = [for i in 1..lengthToAdd -> 0]
				list @ zeroes
			let list1' = extend a.digits (List.length list2)
			let list2' = extend b.digits (List.length list1)
			list1', list2'
		
		let resultDigits = 
			matchLengths a.digits b.digits 
			||> List.zip 
			|> List.map (fun (a,b) -> a + b)
			|> carryDigits
		BigNum(resultDigits)
	
	override this.ToString() =
   		List.fold (fun acc x -> string(x) + acc) "" digits

Leave a Reply