{CoffeeScript} = require 'coffee-script' _ = require 'underscore' CoffeeKup = require 'coffeekup' show = console.log showDocument = (doc, width, height) -> show doc # NOTE Touch or click here to edit this program. Then change # the colors, fonts, ... and see the effect on the book cover. surfaceStyle = back:'silver', front:'#663300' hiddenStyle = back:'darkgrey', front:'grey' [smallFont, bigFont] = ['12px sans-serif','52px sans-serif'] surfaceText = 'CoffeeScript ✵ Scratch Card ✵ ' # Slight obfuscation. The ... converts an array to arguments decode = (nums) -> String.fromCharCode nums... hidden = [{x:15, y:100, font: bigFont, text: decode \ [67,111,102,102,101,101,83,99,114,105,112,116]} {x:80, y:180, font: bigFont, text: decode \ [73,116,39,115,32,106,117,115,116]} {x:30, y:260, font: bigFont, text: decode \ [74,97,118,97,83,99,114,105,112,116,33]} {x:190, y:310, font: smallFont, text: decode \ [68,79,85,66,76,69,80,76,85,83,71,79,79,68]}] draw = (ctx) -> # ctx is an HTML5 Canvas context class Point constructor: (@x, @y) -> drawDisc: (context, style, size = 10) -> context.beginPath() context.fillStyle = style context.arc @x, @y, size, 0, 2*Math.PI, false context.fill() renderInStyle = (context, {back, front}, render) -> background = (context, style) -> context.beginPath() context.fillStyle = style context.fillRect 0, 0, context.canvas.width, context.canvas.height background context, back context.beginPath() context.fillStyle = front render() drawDrabSurface = (context, font, text, style) -> drawRepeatRotated = (angle = 0.2) -> context.save() context.rotate angle context.font = font distance = 2 * parseInt font, 10 textWidth = context.measureText(text).width height = context.canvas.height for x in [0...2 * context.canvas.width] by textWidth for y in [-height...height] by distance context.fillText text, x, y context.restore() renderInStyle context, style, drawRepeatRotated drawPreciousBrush = (context, lines, style) -> createCanvasContext = ({width, height}) -> canvas = document.createElement 'canvas' [canvas.width, canvas.height] = [width, height] canvas.getContext '2d' ctxBrush = createCanvasContext context.canvas renderInStyle ctxBrush, style, -> for {text, font, x, y} in lines ctxBrush.font = font ctxBrush.fillText text, x, y return context.createPattern ctxBrush.canvas, 'repeat' brush = drawPreciousBrush ctx, hidden, hiddenStyle drawDrabSurface ctx, smallFont, surfaceText, surfaceStyle # Hook the brush up to mouse and touch events ctx.canvas.onmousemove = onmove = (evt) -> pos = new Point evt.pageX - ctx.canvas.offsetLeft, evt.pageY - ctx.canvas.offsetTop pos.drawDisc ctx, brush ctx.canvas.ontouchmove = ontouch = (evt) -> evt.preventDefault() # Don't scroll on touch onmove touch for touch in evt.targetTouches return # The CoffeeScript is about ⅔ the size of the JavaScript. # Remove the # sign on the next line to see the JavaScript. # show draw webdesign = -> doctype 5 html -> head -> meta charset: 'utf-8' title 'My drawing | My awesome website' style ''' body {font-family: sans-serif} header, nav, section, footer {display: block} ''' coffeescript -> draw = (ctx, x, y) -> circle = (ctx, x, y) -> ctx.beginPath() ctx.arc x, y, 100, 0, 2*Math.PI, false ctx.stroke() ctx.strokeStyle = 'rgba(255,40,20,0.7)' circle ctx, x, y for angle in [0...2*Math.PI] by 1/3*Math.PI circle ctx, x+100*Math.cos(angle), y+100*Math.sin(angle) window.onload = -> canvas = document.getElementById 'drawCanvas' context = canvas.getContext '2d' draw context, 300, 200 body -> header -> h1 'Seed of Life' canvas id: 'drawCanvas', width: 550, height: 400 kup = if exports? then require 'coffeekup' else CoffeeKup webpage = kup.render webdesign, format:on showDocument webpage, 565, 500 show """ Library Version ------------------ ------------- Coffeescript #{CoffeeScript.VERSION} Underscore #{_.VERSION} CoffeeKup #{CoffeeKup.version} QuickCheck qc.js-2009""" # Show timing for compilation and execution of code blocks @showTiming = off # Variable names that are promoted from the execution # environment to the global environment. These names can # be used in other code blocks than where they are defined. @globalNames = 'Account HTML Rabbit _break _forEach absolute addCats addPoints addToSet between bits blackRabbit catData catNames catRecord deadCats escapeHTML estimatedDistance extractDate extractFootnotes extractMother fatRabbit findReached findRoute footnote forEach hasSevenTruths heightAt htmlDoc image killerRabbit link linkOstrich livingCats livingCats mailArchive makeReachedList makeRoad makeRoads map oldestCat op paragraph paragraphs partial point possibleDirections possibleRoutes power powerRec processParagraph range recluseFile reduce removeFromSet renderFootnote renderHTML renderParagraph retrieveMails roadsFrom route samePoint shortestRoute shortestRouteAbstract simpleObject speak splitParagraph square startsWith storeReached tag traverseRoute weightedDistance yell' # An encoded picture of the signature ostrich. To obtain a base64 # encoding of a file run the CoffeeScript compiler like this: # coffee -e "console.log require('fs').readFileSync('../img/ostrich.jpg').toString 'base64'" @ostrich = ostrich = 'data:image/png;base64,/9j/4AAQSkZJRgABAgAAZABkAAD/7AARRHVja3kAAQAEAAAAHgAA/+4ADkFkb2JlAGTAAAAAAf/bAIQAEAsLCwwLEAwMEBcPDQ8XGxQQEBQbHxcXFxcXHx4XGhoaGhceHiMlJyUjHi8vMzMvL0BAQEBAQEBAQEBAQEBAQAERDw8RExEVEhIVFBEUERQaFBYWFBomGhocGhomMCMeHh4eIzArLicnJy4rNTUwMDU1QEA/QEBAQEBAQEBAQEBA/8AAEQgAWgBaAwEiAAIRAQMRAf/EAIYAAAIDAQEBAAAAAAAAAAAAAAQFAgMGAAEHAQEBAQEBAAAAAAAAAAAAAAACAQMABBAAAgEDAwIDBQYFBQEAAAAAAQIDABEEITESQQVhIhNRcYGhMrFCMxQVBpHBUmJDcqIjUzQ1EQACAgICAgIDAAAAAAAAAAAAARECITFBElFhcaHBIgP/2gAMAwEAAhEDEQA/AMtBEzW3o9ICBUsWG9qPWHTavPa2TZIDENx1qxI7WvRHEA6Uwwe1evxyMg8YL6KPqe32CopZXgAQAUdBBE/l4sQw/EPQ+FNJcjs+DFI6wxsVGq738LtSXJ/c8EbGD0OOPIBqAAyk+weyr19yTt6ITxMjFG3XehJUa2hNTHdcOWVTJo4urno69D764EMGN9msviLXvRiCyATcgLXNBSEnrameQnKhGxz7KdWFoFQsrbmir+J+mq0j81qK9Mf7aU5JBZiMRqaY+cwGYWKqeJA3FKYHKi23zptjIqQmVnLBgeSrp5RsdayssjQNI3AGQgkDUr1tUcvvxkUeivkQeXXa39tC5vcV+kPzXxFiPfQQMUqtYWO5+NKtfKC34LTm5mTxlPKXgeTCwVVHhrQjRnmVD+oSwW4Gt99KIWZorBVFiOJuP6tzTjHxsJceKZQpyI+DWUEkm9yoBsSRtTbjgmxNNjH8xxhQqIyRITtyP0r8qtORLCI1IvLExDBhbS2laTgmH6QCI6yHk6cbt6h6szgdTakWefWeQqi+RmvIu21+Itp8RU3tFiCw5MT8OZCO6huOttakGjlBEZ5eIpbMuRIy2GllBB95ozFj9MlmIUMblR0otJIqZJILtfeifQ+yjsSGOYC2/W1F/p/9w2oyywjLRMVfanU2eZYjBCllIs5OpNht4ClSY8ryqsQJdiAoHtphMpjurqL3N2F9TVbIjNZ6OkpO19Bapfp2Tr6ciyzqvqPDGSzKgsSSdtL6gUz9PEfuOOJyBCWHNjsF9poPP7aqZTMJAOOu41HRgeoNaK8RJy/n2mNrieOSjHkZivIXuQCfcafYYAaOc6kCykai/wBX2mhcbtU57eJuJtIzPbrw2U2rSdiwZAkWRIto7WWMDRr7tY1Hl4DoDZMhsmR52u8YW0SnzctW8NTyoKZLEgLxBbz21BtqSTWpzO0JLOkoJGOtiygnTieVh76Q9/xUSKXJwrLEt/VjB2c25Fajq5KmZ/IzURLfe020/hQP52d2trr93f7K8m5hwQnK4AG++9GxQKncIGVfTuoZhuAaWESLPOtfY8/bjZLLcxFkPW38603n/wCofT4UsTusMYCgC/Wwq39VHyvQ7L8izoSiBgbjQjYjeo5JkWKxJa/QnX505jxhuRQvcsc+g1h40SmYmkBewTfQAmtBidjxpcaOZoNRbmisWI5amyXtb3Vm/VBkCML67bWp3h50UMKoh5MDsGPEDfVSfsrRhXJr8bGUYrLJ5kItxPQW/lV0UsarwH+Py2pR23v8bERyNzhbdGN3S/3lc/Uvv1FB9+7zldqzA2NgPNBoWnflwcW2XhsffST1AX7NQ8SzQsDpyv8AZWRkR1xpo528zlkkVRYctdSPnWsxclMvt2PloCiTIHVTuAwBsay3fHwl/c2JiJzWSfi8jg+Tz3AFvtqXrMNcHVZn82F8ObkqcogRyBNzp0vQqyvLlGeQ2JOi/wBKjYU87/DFFkMuPYq44sx+pjsSvsFIVThLY73/AIVFz5E23Clx4GXM35A1d+Yb29KGRgwtVnE/KiU2CkBaHyQGjYFb6bVR+bFSE4kNuVvGo7I6GYzumPJFO9l4A6hfCgVyJkOjWp73lY/UK3Y3OrEaUhljYagfGtaOUgWUMmmfJGwJ1tsb6i/srYftv92RMq4eawZNk5bgezxFYU71wuCCDYjY0uq+Ayz7XFlYuTF/wlSg0BW1tPdSPuGUkOohDTqvGOZtBY3vyfw3rC9v/cWfgyrIrliNGF/qH91d3HvWZ3NzyuikWKA6W9nuotN40JdV7O7h3P8AMZhkXzKiiOM9NN2+Jr3Fj5ed73NDxQAbijYmCWtpUeoRVPIZGgA2tV1h8qEjmuas9Tx6UIYgh8hlFShmEikN10ub2A9ulUP8KPX/AOZ/i3P4P4m33qOCiPNZg11tx0A1NhQWQxMY06EUc/8A5D/q+Hx8aAyfxW3361tUFtgbC1QNTPwrw/CtDM4UVjRlt/hQw26Uww/o6UWVFuwr0Iz2C163wonF3H00Bl2LgXF2or9NTxojH2G3woj+FZ/tI8H/2Q==' # When changes are made in a code block, only that block is # evaluated. Turning this `off` will cause an evaluation of all # code in the book on *every* keystroke. Only for articles. @incrementalEvaluation = on # Dummy definitions for standalone execution if exports? view = (obj) -> show(obj); obj runOnDemand = confirm = prompt = -> total = 0 count = 1 while count <= 10 total += count count += 1 total total = 0 total += count for count in [1..10] total # Summation using a reduce function sum = (v) -> _.reduce v, ((a,b) -> a+b), 0 sum [1..10] # Summation using reduce attached to Array Array::sum = -> @reduce ((a,b) -> a+b), 0 show [1..10].sum() delete Array::sum 144 9.81 2.998e8 100 + 4 * 11 (100 + 4) * 11 # Compose a solution here 'Patch my boat with chewing gum.' 'The programmer pondered: "0x2b or not 0x2b"' "Aha! It's 43 if I'm not a bit off" "2 + 2 gives #{2 + 2}" 'Imagine if this was a very long line of text' '''First comes A then comes B''' """Math 101: 1 + 1 --- #{1 + 1}""" 'This is the first line\nAnd this is the second' 'A newline character is written like "\\n".' 'con' + 'cat' + 'e' + 'nate' typeof 4.5 -(10 - 2) 3 > 2 3 < 2 100 < 115 < 200 100 < 315 < 200 'Aardvark' < 'Zoroaster' 'Itchy' isnt 'Scratchy' true and false true or false # Compose a solution here (false or true) and not (false and true) true and not false true 1; not false caught = 5 * 5 caught = 4 * 4 luigiDebt = 140 luigiDebt = luigiDebt - 35 show 'Also, your hair is on fire.' show Math.max 2, 4 show 100 + Math.max 7, 4 show Math.max(7, 4) + 100 show Math.max(7, 4 + 100) show Math.max 7, 4 + 100 show Math.PI show console if console? show showDocument show 1 + 2 + 3 + 4 + 5 + view 6 + 7 confirm 'Shall we, then?', (answer) -> show answer prompt 'Tell us everything you know', '...', (answer) -> show 'So you know: ' + answer prompt 'Pick a number', '', (answer) -> theNumber = Number answer show 'Your number is the square root of ' + (theNumber * theNumber) show 0 show 2 show 4 show 6 show 8 show 10 show 12 currentNumber = 0 while currentNumber <= 12 show currentNumber currentNumber = currentNumber + 2 counter = 0 while counter <= 12 then counter = counter + 2 # Compose a solution here result = 1 counter = 0 while counter < 10 result = result * 2 counter = counter + 1 show result # Compose a solution here line = '' counter = 0 while counter < 10 line = line + '#' show line counter = counter + 1 for number in [0..12] by 2 then show number # For with indented body for number in [0..12] by 2 show number # For with prepended body show number for number in [0..12] by 2 # The variable counter, which is about to be defined, # is going to start with a value of 0, which is zero. counter = 0 # Now, we are going to loop, hold on to your hat. while counter < 100 # counter is less than one hundred ### Every time we loop, we INCREMENT the value of counter Seriously, we just add one to it. ### counter++ # And then, we are done. # Compose a solution here result = 1 for counter in [0...10] result = result * 2 show result line = '' for counter in [0...10] line = line + '#' show line for counter in [0..20] if counter % 3 is 0 and counter % 4 is 0 show counter for counter in [0..20] if counter % 4 is 0 show counter if counter % 4 isnt 0 show '(' + counter + ')' for counter in [0..20] if counter % 4 is 0 show counter else show '(' + counter + ')' for counter in [0..20] if counter > 15 show counter + '**' else if counter > 10 show counter + '*' else show counter # Compose a solution here prompt 'You! What is the value of 2 + 2?', '', (answer) -> if answer is '4' show 'You must be a genius or something.' else if answer is '3' or answer is '5' show 'Almost!' else show 'You are an embarrassment.' fun = on show 'The show is on!' unless fun is off current = 20 loop if current % 7 is 0 break current++ show current current = 20 current++ until current % 7 is 0 show current roll = Math.floor Math.random() * 6 + 1 # Compose a solution here luckyNumber = 5 # Choose from 1 to 6 show "Your lucky number is #{luckyNumber}" count = 0 loop show roll = Math.floor Math.random() * 6 + 1 count++ if roll is luckyNumber then break show "Luck took #{count} roll(s)" luckyNumber = 3 # Choose from 1 to 6 show 'Your lucky number is ' + luckyNumber count = 0 until roll is luckyNumber show roll = Math.floor Math.random() * 6 + 1 count++ show 'You are lucky ' + Math.floor(100/count) + '% of the time' show mysteryVariable mysteryVariable = 'nothing' show show 'I am a side effect.' show iam ? undefined iam ?= 'I want to be' show iam iam ?= 'I am already' show iam if iam? show false == 0 show '' == 0 show '5' == 5 show `null === undefined` show `false === 0` show `'' === 0` show `'5' === 5` show 'Apollo' + 5 show null + 'ify' show '5' * 5 show 'strawberry' * 5 show Number('5') * 5 prompt 'What is your name?', '', (input) -> show 'Well hello ' + (input or 'dear') false or show 'I am happening!' true or show 'Not me.' add = (a, b) -> a + b add 2, 2 power = (base, exponent) -> result = 1 for count in [0...exponent] result *= base result power 2, 10 # Compose a solution here absolute = (number) -> if number < 0 -number else number absolute -144 runOnDemand -> # Create the Run button further down testAbsolute = (name, property) -> testPure absolute, [arbInt], name, property testAbsolute 'returns positive integers', (c, arg, result) -> result >= 0 testAbsolute 'positive returns positive', (c, arg, result) -> c.guard arg >= 0 result is arg testAbsolute 'negative returns positive', (c, arg, result) -> c.guard arg < 0 result is -arg test() runOnDemand -> testPure power, [arbInt, arbInt], 'power equals Math.pow for integers', (c, base, exponent, result) -> result is c.note Math.pow base, exponent test() runOnDemand -> testPure power, [arbWholeNum, arbWholeNum], 'power equals Math.pow for positive integers', (c, base, exponent, result) -> result is c.note Math.pow base, exponent test() intensify = (n) -> 2 runOnDemand -> testPure intensify, [arbInt], 'intensify grows by 2 when positive', (c, arg, result) -> c.guard arg > 0 arg + 2 is result testPure intensify, [arbInt], 'intensify grows by 2 when negative', (c, arg, result) -> c.guard arg < 0 arg - 2 is result testPure intensify, [arbConst(0)], 'only non-zero intensify grows', (c, arg, result) -> result is 0 test() intensify = (n) -> if n > 0 n + 2 else if n < 0 n - 2 else n yell = (message) -> view message + '!!' return yell 'Yow' dino = 'I am alive' reptile = 'I am A-OK' meteor = (reptile) -> show reptile # Argument dino = 'I am extinct' reptile = 'I survived' possum = 'I am new' show dino # Outer meteor 'What happened?' show dino # Outer changed show reptile # Outer unchanged try show possum catch e show e.message # Error undefined variable = 'first' # Definition showVariable = -> show 'In showVariable, the variable holds: ' + variable # second testIt = -> variable = 'second' # Assignment show 'In test, the variable holds ' + variable + '.' # second showVariable() show 'The variable is: ' + variable # first testIt() show 'The variable is: ' + variable # second andHere = -> try show aLocal # Not defined catch e then show e.message isHere = -> aLocal = 'aLocal is defined' andHere() isHere() isHere = -> andHere = -> try show aLocal # Is defined catch e then show e.message aLocal = 'aLocal is defined' andHere() isHere() varWhich = 'top-level' parentFunction = -> varWhich = 'local' childFunction = -> show varWhich childFunction child = parentFunction() child() makeAddFunction = (amount) -> add = (number) -> number + amount addTwo = makeAddFunction 2 addFive = makeAddFunction 5 show addTwo(1) + addFive(1) powerRec = (base, exponent) -> if exponent is 0 1 else base * powerRec base, exponent - 1 show 'power 3, 3 = ' + powerRec 3, 3 timeIt = (func) -> start = new Date() for i in [0...1000000] then func() show "Timing: #{(new Date() - start)*0.001}s" add = (n, m) -> n + m # Baseline comparison runOnDemand -> timeIt -> p = add 9,18 # 0.042s timeIt -> p = Math.pow 9,18 # 0.049s timeIt -> p = power 9,18 # 0.464s timeIt -> p = powerRec 9,18 # 0.544s chicken = -> show 'Lay an egg' egg() egg = -> show 'Chick hatched' chicken() # WARNING! Clicking `Run` may cause your # browser to enter a catatonic state runOnDemand -> try show chicken() + ' came first.' catch error then show error.message findSequence = (goal) -> find = (start, history) -> if start is goal history else if start > goal null else find(start + 5, '(' + history + ' + 5)') ? \ find(start * 3, '(' + history + ' * 3)') find 1, '1' show findSequence 24 makeAddFunction = (amount) -> (number) -> number + amount show makeAddFunction(11) 3 # Compose a solution here greaterThan = (x) -> (y) -> y > x greaterThanTen = greaterThan 10 show greaterThanTen 9 yell 'Hello', 'Good Evening', 'How do you do?' yell() console?.log 'R', 2, 'D', 2 text = 'purple haze' show text['length'] show text.length nothing = null try show nothing.length catch error then show error.message cat = colour: 'grey' name: 'Spot' size: 46 # Or: cat = {colour: 'grey', name: 'Spot', size: 46} cat.size = 47 show cat.size delete cat.size show cat.size show cat empty = {} empty.notReally = 1000 show empty.notReally thing = {'gabba gabba': 'hey', '5': 10} show thing['5'] thing['5'] = 20 show thing[2 + 3] delete thing['gabba gabba'] show thing propertyName = 'length' text = 'mainline' show text[propertyName] chineseBox = {} chineseBox.content = chineseBox show 'content' of chineseBox show 'content' of chineseBox.content show chineseBox abyss = {lets:1, go:deep:down:into:the:abyss:7} show abyss show abyss, on # Compose a solution here set = {'Spot': true} # Add 'White Fang' to the set set['White Fang'] = true # Remove 'Spot' delete set['Spot'] # See if 'Asoka' is in the set show 'Asoka' of set object1 = {value: 10} object2 = object1 object3 = {value: 10} show object1 is object2 show object1 is object3 object1.value = 15 show object2.value show object3.value mailArchive = { 'the first e-mail': 'Dear nephew, ...' 'the second e-mail': '...' # and so on ... } mailArchive = { 0: 'Dear nephew, ... (mail number 1)' 1: '(mail number 2)' 2: '(mail number 3)' } for current of mailArchive show 'Processing e-mail #' + current + ': ' + mailArchive[current] mailArchive = ['mail one', 'mail two', 'mail three'] for current in [0...mailArchive.length] show 'Processing e-mail #' + current + ': ' + mailArchive[current] # Compose a solution here range = (upto) -> result = [] i = 0 while i <= upto result[i] = i i++ result show range 4 range = (upto) -> i for i in [0..upto] show range 4 range = (upto) -> [0..upto] show range 4 numbers = (number for number in [0..12] by 2) show numbers doh = 'Doh' show typeof doh.toUpperCase show doh.toUpperCase() mack = [] mack.push 'Mack' mack.push 'the' mack.push 'Knife' show mack.join ' ' show mack.pop() show mack retrieveMails = -> [ "Nephew,\n\nI bought a computer as soon as I received your letter. It took me two days to make it do 'internet', but I just kept calling the nice man at the computer shop, and in the end he came down to help personally. Send me something back if you receive this, so I know whether it actually works.\n\nLove,\nAunt Emily" "Dear Nephew,\n\nVery good! I feel quite proud about being so technologically minded, having a computer and all. I bet Mrs. Goor down the street wouldn't even know how to plug it in, that witch.\n\nAnyway, thanks for sending me that game, it was great fun. After three days, I beat it. My friend Mrs. Johnson was quite worried when I didn't come outside or answer the phone for three days, but I explained to her that I was working with my computer.\n\nMy cat had two kittens yesterday! I didn't even realize the thing was pregnant. I've listed the names at the bottom of my letter, so that you will know how to greet them the next time you come over.\n\nSincerely,\nAunt Emily\n\nborn 15/02/1999 (mother Spot): Clementine, Fireball" "[... and so on ...]\n\nborn 21/09/2000 (mother Spot): Yellow Emperor, Black Leclère" "...\n\nborn 02/04/2001 (mother Clementine): Bugeye, Wolverine, Miss Bushtail" "...\n\ndied 12/12/2002: Clementine\n\ndied 15/12/2002: Wolverine" "...\n\nborn 15/11/2003 (mother Spot): White Fang" "...\n\nborn 10/04/2003 (mother Miss Bushtail): Yellow Bess" "...\n\ndied 30/05/2004: Yellow Emperor" "...\n\nborn 01/06/2004 (mother Miss Bushtail): Catharina, Fat Igor" "...\n\nborn 20/09/2004 (mother Yellow Bess): Doctor Hobbles the 2nd, Noog" "...\n\nborn 15/01/2005 (mother Yellow Bess): The Moose, Liger\n\ndied 17/01/2005: Liger" "Dear nephew,\n\nYour mother told me you have taken up skydiving. Is this true? You watch yourself, young man! Remember what happened to my husband? And that was only from the second floor!\n\nAnyway, things are very exciting here. I have spent all week trying to get the attention of Mr. Drake, the nice gentleman who moved in next\ndoor, but I think he is afraid of cats. Or allergic to them? I am\ngoing to try putting Fat Igor on his shoulder next time I see him, very curious what will happen.\n\nAlso, the scam I told you about is going better than expected. I have already gotten back five 'payments', and only one complaint. It is starting to make me feel a bit bad though. And you are right that it is probably illegal in some way.\n\n(... etc ...)\n\nMuch love,\nAunt Emily\n\ndied 27/04/2006: Black Leclère\n\nborn 05/04/2006 (mother Lady Penelope): Red Lion, Doctor Hobbles the 3rd, Little Iroquois" "...\n\nborn 22/07/2006 (mother Noog): Goblin, Reginald, Little Maggie" "...\n\ndied 13/02/2007: Spot\n\ndied 21/02/2007: Fireball" "...\n\nborn 05/02/2007 (mother Noog): Long-ear Johnson" "...\n\nborn 03/03/2007 (mother Catharina): Asoka, Dark Empress, Rabbitface" ] mailArchive = retrieveMails() for email, i in mailArchive show "Processing e-mail ##{i} #{email[0..15]}..." # Do more things... words = 'Cities of the Interior' show words.split ' ' # Compose a solution here array = ['a', 'b', 'c d'] show array.join(' ').split(' ') paragraph = 'born 15-11-2003 (mother Spot): White Fang' show paragraph.charAt(0) is 'b' and paragraph.charAt(1) is 'o' and paragraph.charAt(2) is 'r' and paragraph.charAt(3) is 'n' show paragraph.slice(0, 4) is 'born' show paragraph[0...4] is 'born' # Compose a solution here startsWith = (string, pattern) -> string.slice(0, pattern.length) is pattern # or startsWith = (string, pattern) -> string[0...pattern.length] is pattern show startsWith 'rotation', 'rot' show 'Pip'.charAt 250 show 'Nop'.slice 1, 10 show 'Pin'[1...10] # Compose a solution here catNames = (paragraph) -> colon = paragraph.indexOf ':' paragraph[colon+2...].split ', ' show catNames 'born 20/09/2004 (mother Yellow Bess): ' + 'Doctor Hobbles the 2nd, Noog' mailArchive = retrieveMails() livingCats = 'Spot': true for email, i in mailArchive paragraphs = email.split '\n' for paragraph in paragraphs if startsWith paragraph, 'born' names = catNames paragraph for name in names livingCats[name] = true else if startsWith paragraph, 'died' names = catNames paragraph for name in names delete livingCats[name] show livingCats, on if 'Spot' in livingCats show 'Spot lives!' else show 'Good old Spot, may she rest in peace.' for cat of livingCats show cat addToSet = (set, values) -> for i in [0..values.length] set[values[i]] = true removeFromSet = (set, values) -> for i in [0..values.length] delete set[values[i]] livingCats = 'Spot': true for email in mailArchive paragraphs = email.split '\n' for paragraph in paragraphs if startsWith paragraph, 'born' addToSet livingCats, catNames paragraph else if startsWith paragraph, 'died' removeFromSet livingCats, catNames paragraph show livingCats, on findLivingCats = -> mailArchive = retrieveMails() livingCats = 'Spot': true handleParagraph = (paragraph) -> if startsWith paragraph, 'born' addToSet livingCats, catNames paragraph else if startsWith paragraph, 'died' removeFromSet livingCats, catNames paragraph for email in mailArchive paragraphs = email.split '\n' for paragraph in paragraphs handleParagraph paragraph livingCats howMany = 0 for cat of findLivingCats() howMany++ show 'There are ' + howMany + ' cats.' whenWasIt = year: 1980, month: 2, day: 1 whenWasIt = new Date 1980, 1, 1 show whenWasIt show new Date show new Date 1980, 1, 1 show new Date 2007, 2, 30, 8, 20, 30 today = new Date(); show "Year: #{today.getFullYear()} month: #{today.getMonth()} day: #{today.getDate()}" show "Hour: #{today.getHours()} minutes: #{today.getMinutes()} seconds: #{today.getSeconds()}" show "Day of week: #{today.getDay()}" today = new Date() show today.getTime() wallFall = new Date 1989, 10, 9 gulfWarOne = new Date 1990, 6, 2 show wallFall < gulfWarOne show wallFall is wallFall # but show wallFall is new Date 1989, 10, 9 wallFall1 = new Date 1989, 10, 9 wallFall2 = new Date 1989, 10, 9 show wallFall1.getTime() is wallFall2.getTime() now = new Date() show now.getTimezoneOffset() # Compose a solution here extractDate = (paragraph) -> numberAt = (start, length) -> Number paragraph[start...start + length] new Date numberAt(11, 4), # Year numberAt( 8, 2) - 1, # Month numberAt( 5, 2) # Day show extractDate 'died 27-04-2006: Black Leclère' catRecord = (name, birthdate, mother) -> name: name birth: birthdate mother: mother addCats = (set, names, birthdate, mother) -> for name in names set[name] = catRecord name, birthdate, mother deadCats = (set, names, deathdate) -> for name in names set[name].death = deathdate 'born 15/11/2003 (mother Spot): White Fang' extractMother = (paragraph) -> start = paragraph.indexOf '(mother ' start += '(mother '.length end = paragraph.indexOf ')' paragraph[start...end] show extractMother \ 'born 15/11/2003 (mother Spot): White Fang' # Compose a solution here between = (string, start, end) -> startAt = string.indexOf start startAt += start.length endAt = string.indexOf end, startAt string[startAt...endAt] show between 'bu ] boo [ bah ] gzz', '[ ', ' ]' extractMother = (paragraph) -> between paragraph, '(mother ', ')' findCats = -> mailArchive = retrieveMails() cats = {'Spot': catRecord 'Spot', new Date(1997, 2, 5), 'unknown'} handleParagraph = (paragraph) -> if startsWith paragraph, 'born' addCats cats, catNames(paragraph), extractDate(paragraph), extractMother(paragraph) else if startsWith paragraph, 'died' deadCats cats, catNames(paragraph), extractDate(paragraph) for email in mailArchive paragraphs = email.split '\n' for paragraph in paragraphs handleParagraph paragraph cats catData = findCats() show catData['Clementine'], on show catData[catData['Clementine'].mother], on formatDate = (date) -> "#{date.getDate()}/" + "#{date.getMonth() + 1}/" + "#{date.getFullYear()}" catInfo = (data, name) -> unless name of data return "No cat by the name of #{name} is known." cat = data[name] message = "#{name}," + " born #{formatDate cat.birth}" + " from mother #{cat.mother}" if "death" of cat message += ", died #{formatDate cat.death}" "#{message}." show catInfo catData, "Fat Igor" # Compose a solution here formatDate = (date) -> pad = (number) -> if number < 10 "0" + number else number "#{pad date.getDate()}/" + "#{pad date.getMonth() + 1}/" + "#{date.getFullYear()}" show formatDate new Date 2000, 0, 1 # Compose a solution here oldestCat = (data) -> oldest = null for name of data cat = data[name] unless 'death' of cat if oldest is null or oldest.birth > cat.birth oldest = cat oldest?.name show oldestCat catData for cat, info of catData # Test with dead cats delete catData[cat] unless 'death' of info show oldestCat catData argumentCounter = -> show "You gave me #{arguments.length} arguments." argumentCounter "Death", "Famine", "Pestilence" print = -> show arg for arg in arguments return print 'From here to', 1/0 add = (number, howmuch) -> if arguments.length < 2 howmuch = 1 number + howmuch show add 6 show add 6, 4 # Compose a solution here range = (start, end) -> if arguments.length < 2 end = start start = 0 result = [] for i in [start..end] result.push i result show range 4 show range 2, 4 # Compose a solution here sum = (numbers) -> total = 0 for num in numbers total += num total show sum [1..10] show sum range 1, 10 for name of Math show name for name of ['Huey', 'Dewey', 'Loui'] show name array = ['Heaven', 'Earth', 'Man'] array.length = 2 show array power = (base, exponent) -> result = 1 for count in [0...exponent] result *= base result between = (string, start, end) -> startAt = string.indexOf start startAt += start.length endAt = string.indexOf end, startAt string[startAt...endAt] show between 'Your mother!', '{-', '-}' between = (string, start, end) -> startAt = string.indexOf start if startAt is -1 then return startAt += start.length endAt = string.indexOf end, startAt if endAt is -1 then return string[startAt...endAt] show between 'bu ] boo [ bah ] gzz', '[ ', ' ]' show between 'bu [ boo bah gzz', '[ ', ' ]' prompt "Tell me something", "", (answer) -> parenthesized = between answer, "(", ")" if parenthesized? show "You parenthesized '#{parenthesized}'." lastElement = (array) -> if array.length > 0 array[array.length - 1] else undefined show lastElement [1, 2, undefined] lastElement = (array) -> if array.length > 0 array[array.length - 1] else throw 'Can not take the last element' + ' of an empty array.' lastElementPlusTen = (array) -> lastElement(array) + 10 try show lastElementPlusTen [] catch error show 'Something went wrong: ' + error currentThing = null processThing = (thing) -> if currentThing isnt null throw 'Oh no! We are already processing a thing!' currentThing = thing # do complicated processing... currentThing = null processThing = (thing) -> if currentThing isnt null throw 'Oh no! We are already processing a thing!' currentThing = thing try # do complicated processing... finally currentThing = null try show Sasquatch catch error show 'Caught: ' + error.message try throw new Error 'Fire!' catch error show error.toString() FoundSeven = {} hasSevenTruths = (object) -> counted = 0 count = (object) -> for name of object if object[name] is true if (++counted) is 7 throw FoundSeven if typeof object[name] is 'object' count object[name] try count object return false catch exception if exception isnt FoundSeven throw exception return true testdata = a: true b: true c: false d: a: true b: false c: true d: a: true b: a: true e: a: false b: true c: true show hasSevenTruths testdata # Say we have: thing = [5..7] doSomething = show # then for i in [0...thing.length] then doSomething thing[i] # or better - you can see the generated code with: show -> \ for element in thing then doSomething element printArray = (array) -> for element in array show element return printArray [7..9] forEach = (array, action) -> for element in array action element #return forEach ['Wampeter', 'Foma', 'Granfalloon'], show runOnDemand -> show forEach # View generated code sum = (numbers) -> total = 0 forEach numbers, (number) -> total += number total show sum [1, 10, 100] negate = (func) -> (x) -> not func x isNotNaN = negate isNaN show isNotNaN NaN show Math.min.apply null, [5, 6] negate = (func) -> -> not func.apply null, arguments morethan = (x,y) -> x > y lessthan = negate morethan show lessthan 5, 7 show Math.min [5, 6]... negate = (func) -> (args...) -> not func args... morethan = (x,y) -> x > y lessthan = negate morethan show lessthan 5, 7 reduce = (array, combine, base) -> forEach array, (element) -> base = combine base, element base add = (a, b) -> a + b sum = (numbers) -> reduce numbers, add, 0 show sum [1, 10, 100] # Compose a solution here countZeroes = (array) -> counter = (total, element) -> total++ if element is 0 total reduce array, counter, 0 bits = [1, 0, 1, 0, 0, 1, 1, 1, 0] show countZeroes bits count = (test, array) -> reduce array, ((total, element) -> total + if test element then 1 else 0), 0 equals = (x) -> (element) -> x is element countZeroes = (array) -> count equals(0), array show countZeroes bits map = (array, func) -> result = [] forEach array, (element) -> result.push func element result show map [0.01, 2, 9.89, Math.PI], Math.round # Leave out result since forEach already returns it map = (array, func) -> forEach array, (element) -> func element # Leave out func arguments map = (array, func) -> forEach array, func # Leave out forEach arguments map = forEach show map [0.01, 2, 9.89, Math.PI], Math.round # You could access a picture with its URL imageSource = 'http://autotelicum.github.com/Smooth-CoffeeScript' linkOstrich = "#{imageSource}/img/ostrich.jpg" # But I will use a predefined image to avoid a server round-trip showDocument """
The connection between the language in which we think/program and the problems and solutions we can imagine is very close. For this reason restricting language features with the intent of eliminating programmer errors is at best dangerous.
-- Bjarne Stroustrup
Mr. Stroustrup is the inventor of the C++ programming language, but quite an insightful person nevertheless.
Also, here is a picture of an ostrich:
""", 565, 420 recluseFile = """ % The Book of Programming %% The Two Aspects Below the surface of the machine, the program moves. Without effort, it expands and contracts. In great harmony, electrons scatter and regroup. The forms on the monitor are but ripples on the water. The essence stays invisibly below. When the creators built the machine, they put in the processor and the memory. From these arise the two aspects of the program. The aspect of the processor is the active substance. It is called Control. The aspect of the memory is the passive substance. It is called Data. Data is made of merely bits, yet it takes complex forms. Control consists only of simple instructions, yet it performs difficult tasks. From the small and trivial, the large and complex arise. The program source is Data. Control arises from it. The Control proceeds to create new Data. The one is born from the other, the other is useless without the one. This is the harmonious cycle of Data and Control. Of themselves, Data and Control are without structure. The programmers of old moulded their programs out of this raw substance. Over time, the amorphous Data has crystallised into data types, and the chaotic Control was restricted into control structures and functions. %% Short Sayings When a student asked Fu-Tzu about the nature of the cycle of Data and Control, Fu-Tzu replied 'Think of a compiler, compiling itself.' A student asked 'The programmers of old used only simple machines and no programming languages, yet they made beautiful programs. Why do we use complicated machines and programming languages?'. Fu-Tzu replied 'The builders of old used only sticks and clay, yet they made beautiful huts.' A hermit spent ten years writing a program. 'My program can compute the motion of the stars on a 286-computer running MS DOS', he proudly announced. 'Nobody owns a 286-computer or uses MS DOS anymore.', Fu-Tzu responded. Fu-Tzu had written a small program that was full of global state and dubious shortcuts. Reading it, a student asked 'You warned us against these techniques, yet I find them in your program. How can this be?' Fu-Tzu said 'There is no need to fetch a water hose when the house is not on fire.' {This is not to be read as an encouragement of sloppy programming, but rather as a warning against neurotic adherence to rules of thumb.} %% Wisdom A student was complaining about digital numbers. 'When I take the root of two and then square it again, the result is already inaccurate!'. Overhearing him, Fu-Tzu laughed. 'Here is a sheet of paper. Write down the precise value of the square root of two for me.' Fu-Tzu said 'When you cut against the grain of the wood, much strength is needed. When you program against the grain of a problem, much code is needed.' Tzu-li and Tzu-ssu were boasting about the size of their latest programs. 'Two-hundred thousand lines', said Tzu-li, 'not counting comments!'. 'Psah', said Tzu-ssu, 'mine is almost a *million* lines already.' Fu-Tzu said 'My best program has five hundred lines.' Hearing this, Tzu-li and Tzu-ssu were enlightened. A student had been sitting motionless behind his computer for hours, frowning darkly. He was trying to write a beautiful solution to a difficult problem, but could not find the right approach. Fu-Tzu hit him on the back of his head and shouted '*Type something!*' The student started writing an ugly solution. After he had finished, he suddenly understood the beautiful solution. %% Progression A beginning programmer writes his programs like an ant builds her hill, one piece at a time, without thought for the bigger structure. His programs will be like loose sand. They may stand for a while, but growing too big they fall apart{Referring to the danger of internal inconsistency and duplicated structure in unorganised code.}. Realising this problem, the programmer will start to spend a lot of time thinking about structure. His programs will be rigidly structured, like rock sculptures. They are solid, but when they must change, violence must be done to them {Referring to the fact that structure tends to put restrictions on the evolution of a program.}. The master programmer knows when to apply structure and when to leave things in their simple form. His programs are like clay, solid yet malleable. %% Language When a programming language is created, it is given syntax and semantics. The syntax describes the form of the program, the semantics describe the function. When the syntax is beautiful and the semantics are clear, the program will be like a stately tree. When the syntax is clumsy and the semantics confusing, the program will be like a bramble bush. Tzu-ssu was asked to write a program in the language called Java, which takes a very primitive approach to functions. Every morning, as he sat down in front of his computer, he started complaining. All day he cursed, blaming the language for all that went wrong. Fu-Tzu listened for a while, and then reproached him, saying 'Every language has its own way. Follow its form, do not try to program as if you were using another language.' """ show # 'The End' paragraphs = recluseFile.split "\n\n" show "Found #{paragraphs.length} paragraphs." # Compose a solution here processParagraph = (paragraph) -> header = 0 while paragraph[0] is '%' paragraph = paragraph.slice 1 header++ type: if header is 0 then 'p' else 'h' + header, content: paragraph show processParagraph paragraphs[0] paragraphs = map recluseFile.split('\n\n'), processParagraph show paragraph for paragraph in paragraphs[0..2] # Compose a solution here splitParagraph = (text) -> # Find character position or end of text indexOrEnd = (character) -> index = text.indexOf character if index is -1 then text.length else index # Return and remove text upto next special # character or end of text takeNormal = -> end = reduce map(['*', '{'], indexOrEnd), Math.min, text.length part = text.slice 0, end text = text.slice end part # Return and remove text upto character takeUpTo = (character) -> end = text.indexOf character, 1 if end is -1 throw new Error 'Missing closing ' + '"' + character + '"' part = text.slice 1, end text = text.slice end + 1 part fragments = []; while text isnt '' if text[0] is '*' fragments.push type: 'emphasised', content: takeUpTo '*' else if text[0] is '{' fragments.push type: 'footnote', content: takeUpTo '}' else fragments.push type: 'normal', content: takeNormal() fragments takeNormalAlternative = -> nextAsterisk = text.indexOf '*' nextBrace = text.indexOf '{' end = text.length if nextAsterisk isnt -1 end = nextAsterisk if nextBrace isnt -1 and nextBrace < end end = nextBrace part = text.slice 0, end text = text.slice end part processParagraph = (paragraph) -> header = 0 while paragraph[0] is '%' paragraph = paragraph.slice 1 header++ type: if header is 0 then 'p' else 'h' + header, content: splitParagraph paragraph # Adhoc test paragraphs = map recluseFile.split('\n\n'), processParagraph show paragraph for paragraph in paragraphs[0..2] extractFootnotes = (paragraphs) -> footnotes = [] currentNote = 0 replaceFootnote = (fragment) -> if fragment.type is 'footnote' ++currentNote footnotes.push fragment fragment.number = currentNote type: 'reference', number: currentNote else fragment forEach paragraphs, (paragraph) -> paragraph.content = map paragraph.content, replaceFootnote footnotes show 'Footnotes from the recluse:' show extractFootnotes paragraphs show paragraphs[20] url = "http://www.gokgs.com/" text = "Play Go!" linkText = "#{text}" show _.escape linkText # Without the _.escape it becomes a link show linkText linkObject = name: 'a' attributes: href: 'http://www.gokgs.com/' content: ['Play Go!'] tag = (name, content, attributes) -> name: name attributes: attributes content: content link = (target, text) -> tag "a", [text], href: target show link "http://www.gokgs.com/", "Play Go!" htmlDoc = (title, bodyContent) -> tag "html", [tag("head", [tag "title", [title]]), tag "body", bodyContent] show htmlDoc "Quote", "In his house at R'lyeh " + "dead Cthulu waits dreaming." # Compose a solution here image = (src) -> tag 'img', [], src: src show image linkOstrich escapeHTML = (text) -> replacements = [[/&/g, '&'] [/"/g, '"'] [//g, '>']] forEach replacements, (replace) -> text = text?.replace replace[0], replace[1] text show escapeHTML '< " & " >' renderHTML = (element) -> pieces = [] renderAttributes = (attributes) -> result = [] if attributes for name of attributes result.push ' ' + name + '="' + escapeHTML(attributes[name]) + '"' result.join '' render = (element) -> # Text node if typeof element is 'string' pieces.push escapeHTML element # Empty tag else if not element.content or element.content.length is 0 pieces.push '<' + element.name + renderAttributes(element.attributes) + '/>' # Tag with content else pieces.push '<' + element.name + renderAttributes(element.attributes) + '>' forEach element.content, render pieces.push '' + element.name + '>' render element pieces.join '' show renderHTML link 'http://www.nedroid.com', 'Drawings!' body = [tag('h1', ['The Test']), tag('p', ['Here is a paragraph ' + 'and an image...']), image(ostrich)] doc = htmlDoc 'The Test', body show renderHTML doc footnote = (number) -> tag 'sup', [link '#footnote' + number, String number] show footnote(42), 3 # Compose a solution here renderFragment = (fragment) -> if fragment.type is 'reference' footnote fragment.number else if fragment.type is 'emphasised' tag 'em', [fragment.content] else if fragment.type is 'normal' fragment.content renderParagraph = (paragraph) -> tag paragraph.type, map paragraph.content, renderFragment show renderParagraph paragraphs[7] renderFootnote = (footnote) -> anchor = tag "a", [], name: "footnote" + footnote.number number = "[#{footnote.number}] " tag "p", [tag("small", [anchor, number, footnote.content])] renderFile = (file, title) -> paragraphs = map file.split('\n\n'), processParagraph footnotes = map extractFootnotes(paragraphs), renderFootnote body = map paragraphs, renderParagraph body = body.concat footnotes renderHTML htmlDoc title, body runOnDemand -> page = renderFile recluseFile, 'The Book of Programming' showDocument page, 565, 500 op = { '+': (a, b) -> a + b '==': (a, b) -> a == b '!': (a) -> !a # and so on } show reduce [1..10], op['+'], 0 add = (a, b) -> a + b show reduce [1..10], add, 0 partial = (func, a...) -> (b...) -> func a..., b... f = (a,b,c,d) -> show "#{a} #{b} #{c} #{d}" g = partial f, 1, 2 g 3, 4 equals10 = partial op['=='], 10 show map [1, 10, 100], equals10 square = (x) -> x * x try show map [[10, 100], [12, 16], [0, 1]], partial map, square # Incorrect catch error show "Error: #{error.message}" partialReverse = (func, a) -> (b) -> func b, a mapSquared = partialReverse map, square show map [[10, 100], [12, 16], [0, 1]], mapSquared show map [[10, 100], [12, 16], [0, 1]], (sublist) -> map sublist, (x) -> x * x negate = (func) -> (args...) -> not func args... compose = (func1, func2) -> (args...) -> func1 func2 args... isUndefined = (value) -> value is undefined isDefined = compose ((v) -> not v), isUndefined show 'isDefined Math.PI = ' + isDefined Math.PI show 'isDefined Math.PIE = ' + isDefined Math.PIE # List functions in Underscore runOnDemand -> show _.functions _ show roads = [ { point1: 'Point Kiukiu', point2: 'Hanaiapa', length: 19 } { point1: 'Point Kiukiu', point2: 'Mt Feani', length: 15 } ] # and so on show roads = 'Point Kiukiu': [ {to: 'Hanaiapa', distance: 19} {to: 'Mt Feani', distance: 15} {to: 'Taaoa', distance: 15} ] 'Taaoa': [ ] # et cetera @roads = {} makeRoad = (from, to, length) -> addRoad = (from, to) -> roads[from] = [] if not (from of roads) roads[from].push to: to, distance: length addRoad from, to addRoad to, from makeRoad 'Point Kiukiu', 'Hanaiapa', 19 makeRoad 'Point Kiukiu', 'Mt Feani', 15 makeRoad 'Point Kiukiu', 'Taaoa', 15 show roads # Compose a solution here makeRoads = (start) -> for i in [1...arguments.length] by 2 makeRoad start, arguments[i], arguments[i + 1] @roads = {} makeRoads 'Point Kiukiu', 'Hanaiapa', 19, 'Mt Feani', 15, 'Taaoa', 15 makeRoads 'Airport', 'Hanaiapa', 6, 'Mt Feani', 5, 'Atuona', 4, 'Mt Ootua', 11 makeRoads 'Mt Temetiu', 'Mt Feani', 8, 'Taaoa', 4 makeRoads 'Atuona', 'Taaoa', 3, 'Hanakee pearl lodge', 1 makeRoads 'Cemetery', 'Hanakee pearl lodge', 6, 'Mt Ootua', 5 makeRoads 'Hanapaoa', 'Mt Ootua', 3 makeRoads 'Puamua', 'Mt Ootua', 13, 'Point Teohotepapapa', 14 show 'Roads from the Airport:' show roads['Airport'] roadsFrom = (place) -> found = roads[place] return found if found? throw new Error "No place named '#{place}' found." try show roadsFrom "Hanaiapa" show roadsFrom "Hanalapa" catch error show "Oops #{error}" gamblerPath = (from, to) -> randomInteger = (below) -> Math.floor Math.random() * below randomDirection = (from) -> options = roadsFrom from options[randomInteger(options.length)].to path = [] loop path.push from break if from is to from = randomDirection from path show gamblerPath 'Hanaiapa', 'Mt Feani' _member = (array, value) -> found = false array.forEach (element) -> if element is value found = true found show _member [6, 7, "Bordeaux"], 7 _break = toString: -> "Break" _forEach = (array, action) -> try for element in array action element catch exception if exception isnt _break throw exception show _forEach [1..3], (n) -> n*n # Which btw could in CoffeeScript be written as show (i*i for i in [1..3]) _member = (array, value) -> found = false _forEach array, (element) -> if element is value found = true throw _break found show _member [6, 7, "Bordeaux"], 7 _member = (array, value) -> found = false for element in array if element is value found = true break found show _member [6, 7, "Bordeaux"], 7 show 7 in [6, 7, "Bordeaux"] _any = (array, test) -> for element in array if test element return true false show _any [3, 4, 0, -3, 2, 1], (n) -> n < 0 show _any [3, 4, 0, 2, 1], (n) -> n < 0 # Using Underscore show _.any [3, 4, 0, -3, 2, 1], (n) -> n < 0 # Redefining member with any _member = (array, value) -> partial = (func, a...) -> (b...) -> func a..., b... _.any array, partial ((a,b) -> a is b), value show _member ["Fear", "Loathing"], "Denial" show _member ["Fear", "Loathing"], "Loathing" _every = (array, test) -> for element in array if not test element return false true show _every [1, 2, 0, -1], (n) -> n isnt 0 show _every [1, 2, -1], (n) -> n isnt 0 show _.every [1, 2, -1], (n) -> n isnt 0 # Using Underscore _flatten = (array) -> result = [] for element in array if _.isArray element result = result.concat _flatten element else result.push element result show _flatten [[1], [2, [3, 4]], [5, 6]] # Using Underscore show _.flatten [[1], [2, [3, 4]], [5, 6]] # Compose a solution here _filter = (array, test) -> result = [] for element in array if test element result.push element result show _filter [0, 4, 8, 12], (n) -> n < 5 isOdd = (n) -> n % 2 isnt 0 show _.filter [0..6], isOdd # Using Underscore possibleRoutes = (from, to) -> findRoutes = (route) -> notVisited = (road) -> not (road.to in route.places) continueRoute = (road) -> findRoutes places: route.places.concat([road.to]), length: route.length + road.distance end = route.places[route.places.length - 1] if end is to [route] else _.flatten _.map _.filter(roadsFrom(end), notVisited), continueRoute findRoutes {places: [from], length: 0} show (possibleRoutes 'Point Teohotepapapa', 'Point Kiukiu').length show possibleRoutes 'Hanapaoa', 'Mt Ootua' # Compose a solution here shortestRoute = (from, to) -> currentShortest = null _.each possibleRoutes(from, to), (route) -> if not currentShortest or currentShortest.length > route.length currentShortest = route currentShortest minimise = (func, array) -> minScore = null found = null _.each array, (element) -> score = func element if minScore is null or score < minScore minScore = score found = element found getProperty = (propName) -> (object) -> object[propName] shortestRouteAbstract = (from, to) -> minimise getProperty('length'), possibleRoutes(from, to) show (shortestRoute 'Point Kiukiu', 'Point Teohotepapapa').places show (shortestRouteAbstract 'Point Kiukiu', 'Point Teohotepapapa').places heightAt = (point) -> heights = [[111,111,122,137,226,192,246,275,285,333,328,264,202,175,151,222,250,222,219,146] [205,186,160,218,217,233,268,300,316,357,276,240,240,253,215,201,256,312,224,200] [228,176,232,258,246,289,306,351,374,388,319,333,299,307,261,286,291,355,277,258] [228,207,263,264,284,348,368,358,391,387,320,344,366,382,372,394,360,314,259,207] [238,237,275,315,353,355,341,332,350,315,283,310,355,350,336,405,361,273,264,228] [245,264,289,340,359,349,336,303,267,259,285,340,315,290,333,372,306,254,220,220] [264,287,331,365,382,381,386,360,299,258,254,284,264,276,295,323,281,233,202,160] [300,327,360,355,365,402,393,343,307,274,232,226,221,262,289,250,252,228,160,160] [343,379,373,337,309,336,378,352,303,290,294,241,176,204,235,205,203,206,169,132] [348,348,364,369,337,276,321,390,347,354,309,259,208,147,158,165,169,169,200,147] [320,328,334,348,354,316,254,315,303,297,283,238,229,207,156,129,128,161,174,165] [297,331,304,283,283,279,250,243,264,251,226,204,155,144,154,147,120,111,129,138] [302,347,332,326,314,286,223,205,202,178,160,172,171,132,118,116,114, 96, 80, 75] [287,317,310,293,284,235,217,305,286,229,211,234,227,243,188,160,152,129,138,101] [260,277,269,243,236,255,343,312,280,220,252,280,298,288,252,210,176,163,133,112] [266,255,254,254,265,307,350,311,267,276,292,355,305,250,223,200,197,193,166,158] [306,312,328,279,287,320,377,359,289,328,367,355,271,250,198,163,139,155,153,190] [367,357,339,330,290,323,363,374,330,331,415,446,385,308,241,190,145, 99, 88,145] [342,362,381,359,353,353,369,391,384,372,408,448,382,358,256,178,143,125, 85,109] [311,337,358,376,330,341,342,374,411,408,421,382,271,311,246,166,132,116,108, 72]] heights[point.y][point.x] show heightAt x: 0, y: 0 show heightAt x: 11, y: 18 weightedDistance = (pointA, pointB) -> heightDifference = heightAt(pointB) - heightAt(pointA) climbFactor = if heightDifference < 0 then 1 else 2 flatDistance = if pointA.x is pointB.x or pointA.y is pointB.y 100 else 141 flatDistance + climbFactor * Math.abs heightDifference show weightedDistance (x: 0, y: 0), (x: 1, y: 1) point = (x, y) -> {x, y} # Same as {x: x, y: y} addPoints = (a, b) -> point a.x + b.x, a.y + b.y samePoint = (a, b) -> a.x is b.x and a.y is b.y show samePoint addPoints(point(10, 10), point(4, -2)), point(14, 8) # Compose a solution here possibleDirections = (from) -> mapSize = 20 insideMap = (point) -> point.x >= 0 and point.x < mapSize and point.y >= 0 and point.y < mapSize directions = [ point(-1, 0), point( 1, 0) point( 0, -1), point( 0, 1) point(-1, -1), point(-1, 1) point( 1, 1), point( 1, -1)] partial = (func, a...) -> (b...) -> func a..., b... _.filter (_.map directions, partial addPoints, from), insideMap show possibleDirections point 0, 0 @BinaryHeap = class BinaryHeap # Public #-------- constructor: (@scoreFunction = (x) -> x) -> @content = [] push: (element) -> # Add the new element to the end of the array. @content.push element # Allow it to bubble up. @_bubbleUp @content.length - 1 pop: -> # Store the first element so we can return it later. result = @content[0] # Get the element at the end of the array. end = @content.pop() # If there are any elements left, put the end # element at the start, and let it sink down. if @content.length > 0 @content[0] = end @_sinkDown 0 result size: -> @content.length remove: (node) -> len = @content.length # To remove a value, we must search through the # array to find it. for i in [0...len] if @content[i] is node # When it is found, the process seen in 'pop' # is repeated to fill up the hole. end = @content.pop() if i isnt len - 1 @content[i] = end if @scoreFunction(end) < @scoreFunction(node) @_bubbleUp i else @_sinkDown i return throw new Error 'Node not found.' # Private #--------- _bubbleUp: (n) -> # Fetch the element that has to be moved. element = @content[n] # When at 0, an element can not go up any further. while n > 0 # Compute the parent element index, and fetch it. parentN = Math.floor((n + 1) / 2) - 1 parent = @content[parentN] # Swap the elements if the parent is greater. if @scoreFunction(element) < @scoreFunction(parent) @content[parentN] = element @content[n] = parent # Update 'n' to continue at the new position. n = parentN # Found a parent that is less, # no need to move it further. else break return _sinkDown: (n) -> # Look up the target element and its score. length = @content.length element = @content[n] elemScore = @scoreFunction element loop # Compute the indices of the child elements. child2N = (n + 1) * 2 child1N = child2N - 1 # This is used to store the new position of # the element, if any. swap = null # If the first child exists (is inside the array)... if child1N < length # Look it up and compute its score. child1 = @content[child1N] child1Score = this.scoreFunction child1 # If the score is less than our elements, # we need to swap. if child1Score < elemScore swap = child1N # Do the same checks for the other child. if child2N < length child2 = @content[child2N] child2Score = @scoreFunction child2 compScore = if swap is null elemScore else child1Score if child2Score < compScore swap = child2N # If the element needs to be moved, # swap it, and continue. if swap isnt null @content[n] = @content[swap] @content[swap] = element n = swap # Otherwise, we are done. else break return heap = new BinaryHeap() _.each [2, 4, 5, 1, 6, 3], (number) -> heap.push number while heap.size() > 0 show heap.pop() # Compose a solution here estimatedDistance = (pointA, pointB) -> dx = Math.abs pointA.x - pointB.x dy = Math.abs pointA.y - pointB.y if dx > dy (dx - dy) * 100 + dy * 141 else (dy - dx) * 100 + dx * 141 show estimatedDistance point(3,3), point(9,6) # Compose a solution here makeReachedList = -> {} storeReached = (list, point, route) -> inner = list[point.x] if inner is undefined inner = {} list[point.x] = inner inner[point.y] = route findReached = (list, point) -> inner = list[point.x] if inner is undefined undefined else inner[point.y] pointID = (point) -> point.x + '-' + point.y makeReachedList = -> {} storeReached = (list, point, route) -> list[pointID(point)] = route findReached = (list, point) -> list[pointID(point)] findRoute = (from, to) -> routeScore = (route) -> if route.score is undefined route.score = route.length + estimatedDistance route.point, to route.score addOpenRoute = (route) -> open.push route storeReached reached, route.point, route open = new BinaryHeap routeScore reached = makeReachedList() addOpenRoute point: from, length: 0 while open.size() > 0 route = open.pop() if samePoint route.point, to return route _.each possibleDirections(route.point), (direction) -> known = findReached reached, direction newLength = route.length + weightedDistance route.point, direction if not known or known.length > newLength if known open.remove known addOpenRoute point: direction, from: route, length: newLength return null route = findRoute point(0, 0), point(19, 19) runOnDemand -> show route traverseRoute = (routes..., func) -> _.each [routes...], (route) -> while route func x: route.point.x y: route.point.y route = route.from showRoute = (routes...) -> traverseRoute routes..., show runOnDemand -> show '\n Easy route' showRoute route show '\n Sightseeing' showRoute findRoute(point( 0, 0), point(11, 17)), findRoute(point(11, 17), point(19, 19)) showSortedRoute = (routes...) -> points = [] traverseRoute routes..., (point) -> points.push point # A sort needs a function that compares two points # Pattern matching can decompose them into unique names points.sort ({x:x1, y:y1}, {x:x2, y:y2}) -> return dx if dx = x1 - x2 return dy if dy = y1 - y2 0 # The Underscore uniq function can get rid of doublets with # help from a function that serializes the relevant properties points = _.uniq points, true, ({x, y}) -> "#{x} #{y}" show point for point in points runOnDemand -> showSortedRoute findRoute(point( 0, 0), point(11, 17)), findRoute(point(11, 17), point(19, 19)) renderRoute = (routes...) -> kup = if exports? then require 'coffeekup' else window.CoffeeKup webdesign = -> doctype 5 html -> head -> style '.map {position: absolute; left: 33px; top: 80px}' body -> header -> h1 'Route' div class: 'map', -> img src: 'http://autotelicum.github.com/' + 'Smooth-CoffeeScript/img/height-small.png' img class: 'map', src: "#{ostrich}", width: size, height: size, \ style: "left:#{x*size}px; top:#{y*size}px" for {x, y} in points points = [] traverseRoute routes..., (point) -> points.push point routePage = kup.render webdesign, locals: size: 500 / 20 # Square map: size divided by fields points: points showDocument routePage, 565, 600 return runOnDemand -> show 'Scenic' renderRoute findRoute(point( 0, 0), point(11, 17)), findRoute(point(11, 17), point(19, 19)) show 'Excursion' renderRoute findRoute(point( 0, 0), point(15, 3)), findRoute(point(15, 3), point(19, 19)) rabbit = {} rabbit.speak = (line) -> show "The rabbit says '#{line}'" rabbit.speak "Well, now you're asking me." speak = (line) -> show "The #{this.adjective} rabbit says '#{line}'" whiteRabbit = adjective: "white", speak: speak fatRabbit = adjective: "fat", speak: speak whiteRabbit.speak "Oh my ears and whiskers, " + "how late it's getting!" fatRabbit.speak "I could sure use a carrot right now." speak.apply fatRabbit, ['Yum.'] speak.call fatRabbit, 'Burp.' class Rabbit constructor: (@adjective) -> speak: (line) -> show "The #{@adjective} rabbit says '#{line}'" whiteRabbit = new Rabbit "white" fatRabbit = new Rabbit "fat" whiteRabbit.speak "Hurry!" fatRabbit.speak "Tasty!" killerRabbit = new Rabbit 'killer' killerRabbit.speak 'GRAAAAAAAAAH!' show killerRabbit makeRabbit = (adjective) -> adjective: adjective speak: (line) -> show adjective + ': ' + line blackRabbit = makeRabbit 'black' show killerRabbit.constructor.name show blackRabbit.constructor.name class WeightyRabbit extends Rabbit constructor: (adjective, @weight) -> super adjective adjustedWeight: (relativeGravity) -> (@weight * relativeGravity).toPrecision 2 tinyRabbit = new WeightyRabbit "tiny", 1.01 jumboRabbit = new WeightyRabbit "jumbo", 7.47 moonGravity = 1/6 jumboRabbit.speak "Carry me, I weigh #{jumboRabbit.adjustedWeight(moonGravity)} stones" tinyRabbit.speak "He ain't heavy, he is my brother" class Account constructor: -> @balance = 0 transfer: (amount) -> @balance += amount getBalance: -> @balance batchTransfer: (amtList) -> for amount in amtList @transfer amount yourAccount = new Account() oldBalance = yourAccount.getBalance() yourAccount.transfer salary = 1000 newBalance = yourAccount.getBalance() show "Books balance: #{salary is newBalance - oldBalance}." class AccountWithFee extends Account fee: 5 transfer: (amount) -> super amount - @fee # feeAccount.transfer @fee yourAccount = new AccountWithFee() oldBalance = yourAccount.getBalance() yourAccount.transfer salary = 1000 newBalance = yourAccount.getBalance() show "Books balance: #{salary is newBalance - oldBalance}." class LimitedAccount extends Account constructor: -> super; @resetLimit() resetLimit: -> @dailyLimit = 50 transfer: (amount) -> if amount < 0 and (@dailyLimit += amount) < 0 throw new Error "You maxed out!" else super amount lacc = new LimitedAccount() lacc.transfer 50 show "Start balance #{lacc.getBalance()}" try lacc.batchTransfer [-1..-10] catch error then show error.message show "After batch balance #{lacc.getBalance()}" class Account constructor: -> @balance = 0 transfer: (amount) -> @balance += amount getBalance: -> @balance batchTransfer: (amtList) -> add = (a,b) -> a+b sum = (list) -> _.reduce list, add, 0 @balance += sum amtList class LimitedAccount extends Account constructor: -> super; @resetLimit() resetLimit: -> @dailyLimit = 50 transfer: (amount) -> if amount < 0 and (@dailyLimit += amount) < 0 throw new Error "You maxed out!" else super amount lacc = new LimitedAccount() lacc.transfer 50 show "Starting with #{lacc.getBalance()}" try lacc.batchTransfer [-1..-10] catch error then show error.message show "After batch balance #{lacc.getBalance()}" simpleObject = {} show simpleObject.constructor.name show simpleObject.toString() show _.methods Rabbit show _.methods Rabbit.prototype show Rabbit.prototype.constructor.name Rabbit.prototype.speak 'I am generic' Rabbit::speak 'I am not initialized' Rabbit::speak 'I am not initialized' show killerRabbit.toString is simpleObject.toString Rabbit::teeth = 'small' show killerRabbit.teeth killerRabbit.teeth = 'long, sharp, and bloody' show killerRabbit.teeth show Rabbit::teeth Rabbit::dance = -> show "The #{@adjective} rabbit dances a jig." killerRabbit.dance() Rabbit = (adjective) -> @adjective = adjective Rabbit::speak = (line) -> show "The #{@adjective} rabbit says '#{line}'" hazelRabbit = new Rabbit "hazel" hazelRabbit.speak "Good Frith!" noCatsAtAll = {} if "constructor" of noCatsAtAll show "Yes, there is a cat called 'constructor'." Object::allProperties = -> for property of this property test = x: 10, y: 3 show test.allProperties() delete Object::allProperties Object::ownProperties = -> for own property of this property test = 'Fat Igor': true, 'Fireball': true show test.ownProperties() delete Object::ownProperties forEachOf = (object, action) -> for own property, value of object action property, value chimera = head: "lion", body: "goat", tail: "snake" forEachOf chimera, (name, value) -> view "The #{name} of a #{value}." forEachIn = (object, action) -> for property of object if Object::hasOwnProperty.call object, property action property, object[property] test = name: "Mordecai", hasOwnProperty: "Uh-oh" forEachIn test, (name, value) -> view "Property #{name} = #{value}" test = name: "Mordecai", hasOwnProperty: "Uh-oh" for own property, value of test show "Property #{property} = #{value}" obj = foo: 'bar' # This test is needed to avoid hidden properties ... show Object::hasOwnProperty.call(obj, 'foo') and Object::propertyIsEnumerable.call(obj, 'foo') # ... because this returns true ... show Object::hasOwnProperty.call(obj, '__proto__') # ... this is required to get false. show Object::hasOwnProperty.call(obj, '__proto__') and Object::propertyIsEnumerable.call(obj, '__proto__') class Dictionary constructor: (@values = {}) -> store: (name, value) -> @values[name] = value lookup: (name) -> @values[name] contains: (name) -> Object::hasOwnProperty.call(@values, name) and Object::propertyIsEnumerable.call(@values, name) each: (action) -> for own property, value of @values action property, value colours = new Dictionary Grover: 'blue' Elmo: 'orange' Bert: 'yellow' show colours.contains 'Grover' colours.each (name, colour) -> view name + ' is ' + colour slash = /\//; show 'AC/DC'.search slash asteriskOrBrace = /[\{\*]/ story = 'We noticed the *giant sloth*, ' + 'hanging from a giant branch.'; show story.search asteriskOrBrace digitSurroundedBySpace = /\s\d\s/ show '1a 2 3d'.search digitSurroundedBySpace notABC = /[^ABC]/ show 'ABCBACCBBADABC'.search notABC # Compose a solution here datePattern = /\d\d\/\d\d\/\d\d\d\d/ show 'born 15/11/2003 (mother Spot): White Fang'\ .search datePattern show /a+/.test 'blah' show /^a+$/.test 'blah' show /cat/.test 'concatenate' show /\bcat\b/.test 'concatenate' parenthesizedText = /\(.*\)/ show "Its (the sloth's) claws were gigantic!"\ .search parenthesizedText datePattern = /\d{1,2}\/\d\d?\/\d{4}/ show 'born 15/11/2003 (mother Spot): White Fang'\ .search datePattern datePattern = /// \d{1,2} # day / # separator \d\d? # month / # separator \d{4} # year /// show 'born 15/11/2003 (mother Spot): White Fang'\ .search datePattern # Compose a solution here mailAddress = /\b[\w\.-]+@[\w\.-]+\.\w{2,3}\b/ mailAddress = /// \b[\w\.-]+ # username @ [\w\.-]+\ # provider . \w{2,3}\b # domain /// show mailAddress.test 'kenny@test.net' show mailAddress.test 'I mailt kenny@tets.nets, ' + 'but it didn wrok!' show mailAddress.test 'the_giant_sloth@gmail.com' cartoonCrying = /boo(hoo+)+/i show "Then, he exclaimed 'Boohoooohoohooo'"\ .search cartoonCrying holyCow = /(sacred|holy) (cow|bovine|bull|taurus)/i show holyCow.test 'Sacred bovine!' show 'No'.match /Yes/ show '... yes'.match /yes/ show 'Giant Ape'.match /giant (\w+)/i quote = "My mind is a swirling miasma " + "(a poisonous fog thought to " + "cause illness) of titilating " + "thoughts and turgid ideas." parenthesized = quote.match /// (\w+) # Word \s* # Whitespace \((.*)\) # Explanation /// if parenthesized isnt null show "Word: #{parenthesized[1]} " + "Explanation: #{parenthesized[2]}" # Compose a solution here extractDate = (string) -> found = string.match /(\d\d?)\/(\d\d?)\/(\d{4})/ if found is null throw new Error "No date found in '#{string}'." new Date Number(found[3]), Number(found[2]) - 1, Number(found[1]) show extractDate \ "born 5/2/2007 (mother Noog): Long-ear Johnson" names = '''Picasso, Pablo Gauguin, Paul Van Gogh, Vincent''' show names.replace /([\w ]+), ([\w ]+)/g, '$2 $1' # Non-printable characters can be tricky in regular # expressions, actually all non-ASCII characters can # be, they can be represented with their numeric codes: # unicode \u0020 = hexadecimal \x20 = ascii #32 = ' ' show names.replace /// ([\w\x20]+) # Lastname ,\u0020 ([\w\x20]+) # Firstname ///g, '$2 $1' eatOne = (match, amount, unit) -> amount = Number(amount) - 1 if amount is 1 unit = unit.slice 0, unit.length - 1 else if amount is 0 unit = unit + 's' amount = 'no' amount + ' ' + unit stock = '1 lemon, 2 cabbages, and 101 eggs' stock = stock.replace /(\d+) (\w+)/g, eatOne show stock # Compose a solution here escapeHTML2 = (text) -> replacements = "<": "<" ">": ">" "&": "&" "\"": """ text.replace /[<>&"]/g, (character) -> replacements[character] ? character show escapeHTML2 "The 'pre-formatted' tag " + "is written \"\"." badWords = ['ape', 'monkey', 'simian', 'gorilla', 'evolution'] pattern = new RegExp badWords.join('|'), 'i' isAcceptable = (text) -> !pattern.test text show isAcceptable 'Mmmm, grapes.' show isAcceptable 'No more of that monkeybusiness, now.' digits = new RegExp '\\d+' show digits.test '101' HTML = tag: (name, content, properties) -> name: name properties: properties content: content link: (target, text) -> HTML.tag 'a', [text], {href: target} # ... many more HTML-producing functions ... # As defined in the prelude globalize = (ns, target = global) -> target[name] = ns[name] for name of ns globalize HTML, window? show link 'http://citeseerx.ist.psu.edu/viewdoc/' + 'download?doi=10.1.1.102.244&rep=rep1&type=pdf', 'What Every Computer Scientist Should Know ' + 'About Floating-Point Arithmetic' range = (start, end, stepSize, length) -> if stepSize is undefined stepSize = 1 if end is undefined end = start + stepSize * (length - 1) result = [] while start <= end result.push start start += stepSize result show range 0, undefined, 4, 5 defaultTo = (object, values) -> for name, value of values if not object.hasOwnProperty name object[name] = value range = (args) -> defaultTo args, {start: 0, stepSize: 1} if args.end is undefined args.end = args.start + args.stepSize * (args.length - 1) result = []; while args.start <= args.end result.push args.start args.start += args.stepSize result show range {stepSize: 4, length: 5} runOnDemand -> eval 'function IamJavaScript() {' + ' alert(\"Repeat after me:' + ' Give me more {();};.\");};' + ' IamJavaScript();' runOnDemand -> CoffeeScript.eval 'alert ((a, b) -> a + b) 3, 4' weatherAdvice = (weather) -> show 'When it is ' + weather switch weather when 'sunny' show 'Dress lightly.' show 'Go outside.' when 'cloudy' show 'Go outside.' when 'tornado', 'hurricane' show 'Seek shelter' else show 'Unknown weather type: ' + weather weatherAdvice 'sunny' weatherAdvice 'cloudy' weatherAdvice 'tornado' weatherAdvice 'hailstorm' for i in [20...30] if i % 3 isnt 0 continue show i + ' is divisible by three.' class Point constructor: (@x, @y) -> pt = new Point 3, 4 {x, y} = pt show "x is #{x} and y is #{y}" firstName = "Alan" lastName = "Turing" name = {firstName, lastName} show name decorate = ({firstName, lastName}) -> show "Distinguished #{firstName} " + "of the #{lastName} family." decorate name pi = π = Math.PI sphereSurfaceArea = (r) -> 4 * π * r * r radius = 1 show '4 * π * r * r when r = ' + radius show sphereSurfaceArea radius evens = (n) -> i for i in [0..n] when i % 2 is 0 show evens 6 steppenwolf = title: 'Tonight at the Magic Theater' warning: 'For Madmen only' caveat: 'Price of Admittance: Your Mind.' caution: 'Not for Everybody.' stipulations = (text for key, text of steppenwolf \ when key in ['warning', 'caveat']) show stipulations show ultimatum for ultimatum in stipulations \ when ultimatum.match /Price/ tautounlogical = "the reason is because I say so" splitStringAt = (str, n) -> [str.substring(0,n), str.substring(n)] [pre, post] = splitStringAt tautounlogical, 14 [pre, post] = [post, pre] # swap show "#{pre} #{post}" [re,mi,fa,sol,la,ti] = [1..6] [dal,ra...,mim] = [ti,re,fa,sol,la,mi] show "#{dal}, #{ra} and #{mim}" [key, word] = if re > ti then [mi, fa] else [fa, mi] show "#{key} and #{word}" class Widget id: 'I am a widget' display: => show @id class Container id: 'I am a container' callback: (f) -> show @id f() a = new Widget a.display() b = new Container b.callback a.display n = 3 f = -> show "Say: 'Yes!'" do f (do -> show "Yes!") while n-- > 0 echoEchoEcho = (msg) -> msg() + msg() + msg() show echoEchoEcho -> "No" runOnDemand -> for i in [1..3] do (i) -> setTimeout (-> show 'With do: ' + i), 0 for i in [1..3] setTimeout (-> show 'Without: ' + i), 0 class BinaryHeap # Public #-------- constructor: (@scoreFunction = (x) -> x) -> @content = [] push: (element) -> # Add the new element to the end of the array. @content.push element # Allow it to bubble up. @_bubbleUp @content.length - 1 pop: -> # Store the first element so we can return it later. result = @content[0] # Get the element at the end of the array. end = @content.pop() # If there are any elements left, put the end # element at the start, and let it sink down. if @content.length > 0 @content[0] = end @_sinkDown 0 result size: -> @content.length remove: (node) -> len = @content.length # To remove a value, we must search through the # array to find it. for i in [0...len] if @content[i] is node # When it is found, the process seen in 'pop' # is repeated to fill up the hole. end = @content.pop() if i isnt len - 1 @content[i] = end if @scoreFunction(end) < @scoreFunction(node) @_bubbleUp i else @_sinkDown i return throw new Error 'Node not found.' # Private #--------- _bubbleUp: (n) -> # Fetch the element that has to be moved. element = @content[n] # When at 0, an element can not go up any further. while n > 0 # Compute the parent element index, and fetch it. parentN = Math.floor((n + 1) / 2) - 1 parent = @content[parentN] # Swap the elements if the parent is greater. if @scoreFunction(element) < @scoreFunction(parent) @content[parentN] = element @content[n] = parent # Update 'n' to continue at the new position. n = parentN # Found a parent that is less, # no need to move it further. else break return _sinkDown: (n) -> # Look up the target element and its score. length = @content.length element = @content[n] elemScore = @scoreFunction element loop # Compute the indices of the child elements. child2N = (n + 1) * 2 child1N = child2N - 1 # This is used to store the new position of # the element, if any. swap = null # If the first child exists (is inside the array)... if child1N < length # Look it up and compute its score. child1 = @content[child1N] child1Score = this.scoreFunction child1 # If the score is less than our elements, # we need to swap. if child1Score < elemScore swap = child1N # Do the same checks for the other child. if child2N < length child2 = @content[child2N] child2Score = @scoreFunction child2 compScore = if swap is null elemScore else child1Score if child2Score < compScore swap = child2N # If the element needs to be moved, # swap it, and continue. if swap isnt null @content[n] = @content[swap] @content[swap] = element n = swap # Otherwise, we are done. else break return (exports ? this).BinaryHeap = BinaryHeap runOnDemand -> sortByValue = (obj) -> _.sortBy obj, (n) -> n buildHeap = (c, a) -> heap = new BinaryHeap heap.push number for number in a c.note heap declare 'heap is created empty', [], (c) -> c.assert (new BinaryHeap).size() is 0 declare 'heap pop is undefined when empty', [], (c) -> c.assert _.isUndefined (new BinaryHeap).pop() declare 'heap contains number of inserted elements', [arbArray(arbInt)], (c, a) -> c.assert buildHeap(c, a).size() is a.length declare 'heap contains inserted elements', [arbArray(arbInt)], (c, a) -> heap = buildHeap c, a c.assert _.isEqual sortByValue(a), \ sortByValue(heap.content) declare 'heap pops elements in sorted order', [arbArray(arbInt)], (c, a) -> heap = buildHeap c, a for n in sortByValue a then c.assert n is heap.pop() c.assert heap.size() is 0 declare 'heap does not remove non-existent elements', [arbArray(arbInt), arbInt], expectException (c, a, b) -> if b in a then c.guard false heap = buildHeap c, a heap.remove b declare 'heap removes existing elements', [arbArray(arbInt), arbInt], (c, a, b) -> if not (b in a) then c.guard false aSort = sortByValue _.without a, b count = a.length - aSort.length heap = buildHeap c, a heap.remove b for i in [0...count] for n in aSort then c.assert n is heap.pop() c.assert heap.size() is 0 test() # CoffeeScript runOnDemand -> start = new Date() N = 1000000 a = Array(N) for i in [0...N] a[i] = Math.random() s = 0 for v in a s += v t = 0 for v in a t += v*v t = Math.sqrt t duration = new Date() - start show "N: #{N} in #{duration*0.001} s" show "Result: #{s} and #{t}" # CoffeeScript encoding: utf-8 # Create variations to try permute = (L) -> n = L.length return ([elem] for elem in L) if n is 1 [a, L] = [ [L[0]], L.slice 1 ] result = [] for p in permute L for i in [0...n] result.push p[...i].concat a, p[i...] result # Check a variation test = (p, n) -> for i in [0...n - 1] for j in [i + 1...n] d = p[i] - p[j] return true if j - i is d or i - j is d false # N queens solver nQueen = (n) -> result = [] for p in permute [0...n] result.push p unless test p, n result # Repeat a string a number of times rep = (s, n) -> (s for [0...n]).join '' # Display a board with a solution printBoard = (solution) -> board = "\n" end = solution.length for pos, row in solution board += "#{end - row} #{rep ' ☐ ', pos} " + "♕ #{rep ' ☐ ', end - pos - 1}\n" # Using radix 18 hack! board += ' ' + (n.toString 18 \ for n in [10...18]).join(' ').toUpperCase() board + "\n" # Find all solutions solve = (n) -> for solution, count in nQueen n show "Solution #{count+1}:" show printBoard solution count runOnDemand -> start = new Date() solve 8 # Normal chessboard size show "Timing: #{(new Date() - start)*0.001}s"