appendix B

Answers to exercises

Exercise 2.3.3

torch = price: 21
umbrella = {}
combinedCost = (torch.price || 0) + (umbrella.price || 0)
# 21





Exercise 2.4.4

animal = "crocodile"
collective = switch animal
  when "antelope" then "herd"
  when "baboon" then "rumpus"
  when "badger" then "cete"
  when "cobra" then "quiver"
  when "crocodile" then "bask"
# bask





Exercise 2.5.3

animal = "cobra"
collective = switch animal
  when "antelope" then "herd"
  when "baboon" then "rumpus"
  when "badger" then "cete"
  when "cobra" then "quiver"
  when "crocodile" then "bask"
"The collective of #{animal} is #{collective}"
# The collective of cobra is quiver





Exercise 2.6.5

animals = 'baboons badgers antelopes cobras crocodiles'
result = for animal in animals.split " " collective = switch animal when "antelopes" then "herd" when "baboons" then "rumpus" when "badgers" then "cete" when "cobras" then "quiver" when "crocodiles" then "bask" "A #{collective} of #{animal}"





Exercise 3.1.5

countWords = (text) ->
  words = text.split /[\s,]/
  significantWords = (word for word in words when word.length > 3)
significantWords.length
everyOtherWord = (text) -> words = text.split /[\s,]/ takeOther = for word, index in words if index % 2 then "" else word takeOther.join(" ").replace /\s\s/gi, " "





Exercise 3.3.4

http = require 'http'
fs = require 'fs'
sourceFile = 'attendees'
fileContents = 'File not read yet.'
readSourceFile = -> fs.readFile sourceFile, 'utf-8', (error, data) -> if error console.log error else
fileContents = data
fs.watchFile sourceFile, readSourceFile
countWords = (text) ->
text.split(/,/gi).length
readSourceFile sourceFile
server = http.createServer (request, response) ->
response.end "#{countWords(fileContents)}"
server.listen 8080, '127.0.0.1'





Exercise 3.4.4

accumulate = (initial, items, accumulator) ->
  total = initial
  for item in items
    total = accumulator total, item
total
sumFractions = (fractions) -> accumulator = (lhs, rhs) -> if lhs is '0/0' rhs else if rhs is '0/0' lhs else lhsSplit = lhs.split /\//gi rhsSplit = rhs.split /\//gi lhsNumer = 1*lhsSplit[0] lhsDenom = 1*lhsSplit[1] rhsNumer = 1*rhsSplit[0] rhsDenom = 1*rhsSplit[1] if lhsDenom isnt rhsDenom commonDenom = lhsDenom*rhsDenom else
commonDenom = lhsDenom
sumNumer = lhsNumer*(commonDenom/lhsDenom) + rhsNumer*(commonDenom/rhsDenom)
"#{sumNumer}/#{commonDenom}"
accumulate '0/0', fractions, accumulator
console.log sumFractions ['2/6', '1/4'] # '14/24'





keep = (arr, cond) ->
  item for item in arr when cond item





Exercise 3.4.4

phonebook =
  numbers:
    hannibal: '555-5551'
    darth: '555-5552'
    hal9000: 'disconnected'
    freddy: '555-5554'
    'T-800': '555-5555'
  list: ->
    "#{name}: #{number}" for name, number of @numbers
  add: (name, number) ->
    if not (name of @numbers)
      @numbers[name] = number
    else
      "#{name} already exists"
  edit: (name, number) ->
    if name of @numbers
      @numbers[name] = number
    else
      "#{name} not found"
  get: (name) ->
    if name of @numbers
      "#{name}: #{@numbers[name]}"
    else
"#{name} not found"
console.log "Phonebook. Commands are add, get, edit, list, and exit."
process.stdin.setEncoding 'utf8'
stdin = process.openStdin()
stdin.on 'data', (chunk) -> args = chunk.split ' ' command = args[0].trim() name = args[1].trim() if args[1] number = args[2].trim() if args[2] switch command when 'add' res = phonebook.add(name, number) if name and number console.log res when 'get' console.log phonebook.get(name) if name when 'edit' console.log phonebook.edit(name, number) if name and number when 'list' console.log phonebook.list() when 'exit' process.exit 1





css = (element, styles) ->
  element.style ?= {}
  for key, value of styles
element.style[key] = value
class Element div = new Element
css div, width: 10
div.style.width # 10





Exercise 4.6.3

cassette =
  title: "Awesome songs. To the max!"
  duration: "10:34"
  released: "1988"
  track1: "Safety Dance - Men Without Hats"
  track2: "Funkytown - Lipps, Inc"
  track3: "Electric Avenue - Eddy Grant"
  track4: "We built this city - Starship"





musicDevice = Object.create cassette





secondMusicDevice = Object.create musicDevice





cassette.track5 = "Hello - Lionel Richie"
secondMusicDevice.track5
# "Hello - Lionel Richie"
musicDevice.track6 = "Mickey - Toni Basil"
secondMusicDevice.track6 # "Mickey - Toni Basil"





Exercise 4.7.2

views =
  excluded: []
  pages: {}
  clear: ->
    @pages = {}
  increment: (key) ->
   unless key in @excluded
     @pages[key] ?= 0
     @pages[key] = @pages[key] + 1
  ignore: (page) ->
      @excluded = @excluded.concat page
  total: ->
    sum = 0
    for own page, count of @pages
      sum = sum + count
    sum





Exercise 4.8.3

class GranTurismo
  constructor: (options) ->
    @options = options
  summary: ->
("#{key}: #{value}" for key, value of @options).join "\n"
options = wheels: 'phat'
dice: 'fluffy'
scruffysGranTurismo = new GranTurismo options
scruffysGranTurismo.summary() # wheels: phat # dice: fluffy





class GranTurismo
  constructor: (@options) ->
   summary: ->
     ("#{key}: #{value}" for key, value of @options).join "\n"





Exercise 5.3.3

class Product
# any implementation of Product
class Camera extends Product cameras = [] @alphabetical = -> cameras.sort (a, b) -> a.name > b.name constructor: -> all.push @ super





Exercise 5.8.1

fs = require 'fs'
http = require 'http'
url = require 'url'
coffee = require 'coffee-script'
class ShopServer constructor: (@host, @port, @shopData, @shopNews) -> @css = '' fs.readFile './client.css', 'utf-8', (err, data) => if err then throw err
@css = data
readClientScript: (callback) -> script = "./client.coffee" fs.readFile script, 'utf-8', (err, data) -> if err then throw err
callback data
headers: (res, status, type) ->
res.writeHead status, 'Content-Type': "text/#{type}"
renderView: -> """ <!doctype html> <head> <title>Agtron's Emporium</title> <link rel='stylesheet' href='/css/client.css' /> </head> <body> <div class='page'> <h1>----Agtron’s Emporium----</h1> <script src='/js/client.js'></script> </div> </body> </html>
"""
handleClientJs: (path, req, res) -> @headers res, 200, 'javascript' writeClientScript = (script) -> res.end coffee.compile(script)
@readClientScript writeClientScript
handleClientCss: (path, req, res) -> @headers res, 200, 'css'
res.end @css
handleImage: (path, req, res) -> fs.readFile ".#{path}", (err, data) => if err @headers res, 404, 'image/png' res.end() else @headers res, 200, 'image/png'
res.end data, 'binary'
handleJson: (path, req, res) -> switch path when '/json/list' @headers res, 200, 'json' res.end JSON.stringify(@shopData) when '/json/list/camera' @headers res, 200, 'json' camera = @shopData.camera res.end JSON.stringify(camera) when '/json/news' @headers res, 200, 'json' res.end JSON.stringify(@shopNews) else @headers res, 404, 'json'
res.end JSON.stringify(status: 404)
handlePost: (path, req, res) -> category = /^\/json\/purchase\/([^/]*)\/([^/]*)$/.exec(path)?[1] item = /^\/json\/purchase\/([^/]*)\/([^/]*)$/.exec(path)?[2] if category? and item? and data[category][item].stock > 0 data[category][item].stock -= 1 @headers res, 200, 'json' res.write JSON.stringify status: 'success', update: data[category][item] else res.write JSON.stringify status: 'failure'
res.end()
handleGet: (path, req, res) -> if path is '/' @headers res, 200, 'html' res.end @renderView() else if path.match /\/json/ @handleJson path, req, res else if path is '/js/client.js' @handleClientJs path, req, res else if path is '/css/client.css' @handleClientCss path, req, res else if path.match /^\/images\/(.*)\.png$/gi @handleImage path, req, res else @headers res, 404, 'html'
res.end '404'
start: -> @httpServer = http.createServer (req, res) => path = url.parse(req.url).pathname if req.method == "POST" @handlePost path, req, res else
@handleGet path, req, res
@httpServer.listen @port, @host, =>
console.log "Running at #{@host}:#{@port}"
stop: ->
@httpServer?.close()
data = require('./data').all news = require('./news').all
shopServer = new ShopServer '127.0.0.1', 9999, data, news
shopServer.start()





Exercises 7.2.5

swapPairs = (array) ->
  for index in array by 2
    [first, second] = array[index-1..index]
    [second, first]





swapPairs([3,4,3,4,3,4])
# [ [ 4, 3 ], [ 4, 3 ], [ 4, 3 ] ]
swapPairs([1,2,3,4,5,6]) # [ [ 2, 1 ], [ 4, 3 ], [ 6, 5 ] ]





swapPairs = (array) ->
  reversedPairs = for index in array by 2
    [first, second] = array[index-1..index]
    [second, first]
[].concat reversedPairs...
swapPairs([3,4,3,4,3,4])
# [ 4, 3, 4, 3, 4, 3 ]
swapPairs([1,2,3,4,5,6]) # [ 2, 1, 4, 3, 6, 5 ]





phoneDirectory =
  A: [
      name: 'Abe'
      phone: '555 1110'
    ,
      name: 'Andy'
      phone: '555 1111'
    ,
      name: 'Alice'
      phone: '555 1112'
  ]
  B: [
      name: 'Bam'
      phone: '555 1113'
]
lastNumberForLetter = (letter, directory) -> [..., lastForLetter] = directory[letter] {phone} = lastForLetter
phone
lastNumberForLetter 'A', phoneDirectory # 555 1112





Exercises 10.4.4

class Tracking
  constructor: (prefs, http) ->
    @http = http
  start: ->
@http.listen()
http = listen: ->





double = (original) ->
  mock = {}
  for key, value of original
    if value.call?
      do ->
        stub = ->
          stub.called = true
        mock[key] = stub
  mock





double = (original) ->
  spy = Object.create original
  for key, value of original
    if value.call?
      do ->
        originalMethod = value
        spyMethod = (args...) ->
          spyMethod.called = true
          originalMethod args...
        spy[key] = spyMethod
  spy