exercism / coffeescript

Exercism exercises in CoffeeScript.
https://exercism.org/tracks/coffeescript
MIT License
19 stars 50 forks source link

Building a training set of tags for coffeescript #233

Closed ErikSchierboom closed 7 months ago

ErikSchierboom commented 8 months ago

Hello lovely maintainers :wave:

We've recently added "tags" to student's solutions. These express the constructs, paradigms and techniques that a solution uses. We are going to be using these tags for lots of things including filtering, pointing a student to alternative approaches, and much more.

In order to do this, we've built out a full AST-based tagger in C#, which has allowed us to do things like detect recursion or bit shifting. We've set things up so other tracks can do the same for their languages, but its a lot of work, and we've determined that actually it may be unnecessary. Instead we think that we can use machine learning to achieve tagging with good enough results. We've fine-tuned a model that can determine the correct tags for C# from the examples with a high success rate. It's also doing reasonably well in an untrained state for other languages. We think that with only a few examples per language, we can potentially get some quite good results, and that we can then refine things further as we go.

I released a new video on the Insiders page that talks through this in more detail.

We're going to be adding a fully-fledged UI in the coming weeks that allow maintainers and mentors to tag solutions and create training sets for the neural networks, but to start with, we're hoping you would be willing to manually tag 20 solutions for this track. In this post we'll add 20 comments, each with a student's solution, and the tags our model has generated. Your mission (should you choose to accept it) is to edit the tags on each issue, removing any incorrect ones, and add any that are missing. In order to build one model that performs well across languages, it's best if you stick as closely as possible to the C# tags as you can. Those are listed here. If you want to add extra tags, that's totally fine, but please don't arbitrarily reword existing tags, even if you don't like what Erik's chosen, as it'll just make it less likely that your language gets the correct tags assigned by the neural network.


To summarise - there are two paths forward for this issue:

  1. You're up for helping: Add a comment saying you're up for helping. Update the tags some time in the next few days. Add a comment when you're done. We'll then add them to our training set and move forward.
  2. You not up for helping: No problem! Just please add a comment letting us know :)

If you tell us you're not able/wanting to help or there's no comment added, we'll automatically crowd-source this in a week or so.

Finally, if you have questions or want to discuss things, it would be best done on the forum, so the knowledge can be shared across all maintainers in all tracks.

Thanks for your help! :blue_heart:


Note: Meta discussion on the forum

ErikSchierboom commented 8 months ago

Exercise: hello-world

Code

helloWorld = ->
  hello: (name) ->
    return "Hello, #{name || 'World'}!"

module.exports = helloWorld

Tags:

construct:string-interpolation
construct:assignment
construct:code-structure
construct:method
construct:module
construct:nested-function
construct:object
construct:parameter
construct:return
construct:ternary
construct:variable
paradigm:functional
paradigm:imperative
paradigm:object-oriented
technique:string-interpolation
ErikSchierboom commented 8 months ago

Exercise: hello-world

Code

class HelloWorld

  hello: (name='World') ->
    "Hello, #{ name }!"

module.exports = HelloWorld

Tags:

construct:class
construct:constructor
construct:method
construct:optional-parameter
construct:parameter
construct:string
construct:template-string
construct:visibility-modifiers
paradigm:object-oriented
ErikSchierboom commented 8 months ago

Exercise: hello-world

Code

class HelloWorld

    hello: (name) ->

        if name 
            "Hello, " + name + "!"
        else 
            "Hello, World!"

module.exports = HelloWorld

Tags:

construct:class
construct:method
construct:string
construct:visibility-modifiers
paradigm:object-oriented
ErikSchierboom commented 8 months ago

Exercise: bob

Code

class Bob
  hey: (s) ->
    switch
      when s.match(/^\s*$/)
        "Fine. Be that way!"
      when s.match(/^[^a-z]+$/)
        "Woah, chill out!"
      when s.match(/\?$/)
        "Sure."
      else
        "Whatever."

module.exports = Bob

Tags:

construct:class
construct:method
construct:module
construct:property
construct:regexp
construct:string
construct:switch
construct:when
construct:visibility-modifiers
paradigm:object-oriented
technique:regular-expression
uses:RegExp
ErikSchierboom commented 8 months ago

Exercise: word-count

Code

class Words
  constructor: (@words) ->
    @count = {}
    for word in @_sanitizedWords()
      @_addWord word

  _addWord: (word) ->
    @count[word] ||= 0
    @count[word] += 1

  _sanitizedWords: ->
    @words.replace(/\W+/g," ").trim().split(/\W+/).map (w) ->
      w.toLowerCase()

module.exports = Words

Tags:

construct:assignment
construct:class
construct:constructor
construct:field
construct:for-loop
construct:function
construct:implicit-constructor
construct:invocation
construct:lambda
construct:map
construct:method
construct:number
construct:object
construct:parameter
construct:property
construct:regular-expression
construct:string
construct:method-invocation
construct:variable
construct:visibility-modifiers
paradigm:functional
paradigm:object-oriented
technique:higher-order-functions
technique:looping
technique:regular-expression
ErikSchierboom commented 8 months ago

Exercise: word-count

Code

class Words
  constructor: (@phrase) ->
    @count = countWords(@phrase.toLowerCase().split(/\W+/))

countWords = (wordArray) ->
  result = {}
  for word in wordArray when word != ''
    result[word] = 0 if not result[word]
    result[word] += 1
  result

module.exports = Words

Tags:

construct:class
construct:constructor
construct:for-in
construct:if
construct:invocation
construct:method
construct:number
construct:object
construct:parameter
construct:property
construct:regex
construct:variable
construct:visibility-modifiers
paradigm:object-oriented
technique:looping
technique:regular-expression
ErikSchierboom commented 8 months ago

Exercise: accumulate

Code

Array::accumulate = (callback) -> callback value for value in this

Tags:

construct:class
construct:method
construct:parameter
construct:prototype
construct:throw
construct:visibility-modifiers
paradigm:object-oriented
technique:exceptions
ErikSchierboom commented 8 months ago

Exercise: binary

Code

class Binary

  constructor: (bin) ->
    @bin = bin

  toDecimal: ->
    if @bin == "carrot"
      0
    else
      binArray = @bin.split("").reverse()
      base2 = (@convert i, char for char, i in binArray)
      base2.reduce (x, y) -> x + y

  convert: (i, char) ->
    num = parseInt(char)
    num * (2**i)

module.exports = Binary

Tags:

construct:add
construct:assignment
construct:class
construct:constructor
construct:double
construct:else
construct:if
construct:implicit-conversion
construct:invocation
construct:lambda
construct:method
construct:multiply
construct:number
construct:parameter
construct:property
construct:string
construct:visibility-modifiers
paradigm:functional
paradigm:object-oriented
technique:higher-order-functions
ErikSchierboom commented 8 months ago

Exercise: trinary

Code

class Trinary
  constructor: (@trinary_string) ->

  toDecimal: ->
    accumulator = 0
    if @valid_trinary_string()
      for digit, index in @digits_by_ascending_rank()
        accumulator += parseInt(digit) * Math.pow(3, index)
    accumulator

  digits_by_ascending_rank: ->
    @trinary_string.match(/./g).reverse()

  valid_trinary_string: ->
    /^[0-2]+$/.test(@trinary_string)

module.exports = Trinary

Tags:

construct:class
construct:constructor
construct:for-in
construct:if
construct:invocation
construct:method
construct:multiply
construct:number
construct:parameter
construct:property
construct:regex
construct:string
construct:variable
construct:visibility-modifiers
paradigm:object-oriented
technique:looping
technique:regular-expression
ErikSchierboom commented 8 months ago

Exercise: hexadecimal

Code

class Hexadecimal
  @hexCodes = '0123456789abcdef'

  constructor: (hex) ->
    @hex = hex.toLowerCase()

  toDecimal: ->
    digits = @hex.split("")
    decimal = digits.reduce (total, digit) ->
      total * 16 + digitToDecimal(digit)
    , 0
    if isNaN(decimal) then 0 else decimal

  digitToDecimal = (digit) =>
    index = @hexCodes.indexOf(digit)
    if index == -1 then NaN else index

module.exports.Hexadecimal = Hexadecimal

Tags:

construct:add
construct:assignment
construct:class
construct:constructor
construct:field
construct:if
construct:invocation
construct:method
construct:number
construct:parameter
construct:property
construct:string
construct:ternary
construct:variable
construct:visibility-modifiers
paradigm:object-oriented
ErikSchierboom commented 8 months ago

Exercise: hexadecimal

Code

class Hexadecimal
    constructor: (hex) ->
      if(hex.match(/^[0-9a-f]+$/))
          @decimal = parseInt(hex, 16)
        else
          @decimal = 0

    toDecimal : -> @decimal

module.exports = Hexadecimal

Tags:

construct:class
construct:constructor
construct:method
construct:parameter
construct:property
construct:regex
construct:string
construct:visibility-modifiers
paradigm:object-oriented
technique:regular-expression
ErikSchierboom commented 8 months ago

Exercise: hexadecimal

Code

NumeralSystem = require '../binary/binary'

colors =
  aqua:   '00ffff', black: '000000', blue:   '0000ff', fuchsia: 'ff00ff'
  gray:   '808080', green: '008000', lime:   '00ff00', maroon:  '800000'
  navy:   '000080', olive: '808000', purple: '800080', red:     'ff0000'
  silver: 'c0c0c0', teal:  '008080', white:  'ffffff', yellow:  'ffff00'

module.exports =
  class Hexadecimal extends NumeralSystem
    symbols: '0123456789abcdef'

    constructor: (name) ->
      @numeral = colors[name] or name

      super

Tags:

construct:assignment
construct:class
construct:coffee-script
construct:constructor
construct:extend
construct:field
construct:invocation
construct:method
construct:module
construct:named-argument
construct:object
construct:or
construct:property
construct:string
construct:super
construct:variable
construct:visibility-modifiers
paradigm:object-oriented
ErikSchierboom commented 8 months ago

Exercise: nth-prime

Code

class Prime
  @nth: (n) ->
    throw "Prime is not possible" if n == 0
    lastPrime = 0
    counter = 0
    while counter < n
      counter += 1 if @isPrime(lastPrime)
      lastPrime += 1
    return lastPrime - 1

  @isPrime: (candidate) ->
    return false if candidate == 1
    return true if candidate == 2
    return false if @isEven(candidate)

    counter = 3
    max = candidate * 0.5 + 1
    while counter < max
      return false if candidate % counter == 0
      counter += 2
    return true

  @isEven: (n) ->
    return n % 2 == 0

module.exports = Prime

Tags:

construct:add
construct:assignment
construct:class
construct:constructor
construct:if
construct:implicit-conversion
construct:invocation
construct:method
construct:multiply
construct:number
construct:parameter
construct:return
construct:string
construct:subtract
construct:throw
construct:variable
construct:visibility-modifiers
construct:while-loop
paradigm:object-oriented
technique:exceptions
technique:looping
ErikSchierboom commented 8 months ago

Exercise: palindrome-products

Code

class Palindrome
  constructor: (a,b)->
    @value = a*b
    @factors = [[a,b]]
    @isValid = Palindrome.isValid(this.value)
  insert: (a,b)->
    @factors.push [a,b].sort() if @value == a*b
  @isValid: (value)->
    valString = value+""
    valString == valString.split('').reverse().join('')
  @compare: (a,b)->
    if a.value < b.value then -1 else if a.value > b.value then 1 else 0

class Palindromes
  constructor: (options = {})->
    @max = options.maxFactor ? 10
    @min = options.minFactor ? 1
    @palindromes = []
  generate: ->
    @palindromes = objToArray(generatePalindromes(this)).sort(Palindrome.compare)
  smallest: -> @palindromes[0];
  largest: -> @palindromes[@palindromes.length-1];
  generatePalindromes = (options)->
    palindromes = {}
    eachPalindromeCombination options.min,options.max, (a,b,value)->
      palindromes[value] = new Palindrome(a,b) unless palindromes[value]?.insert(a,b)
    palindromes
  eachPalindromeCombination = (min,max,callback)->
    for a in [min..max]
      for b in [a..max] when Palindrome.isValid(value = a*b)
        callback(a,b,value)
        [a,b]
  objToArray = (obj)->
    val for key,val of obj

module.exports = Palindromes

Tags:

construct:class
construct:constructor
construct:method
construct:assignment
construct:method-invocation
construct:if
construct:ternary
construct:parameter
construct:optional-parameter
construct:number
construct:property
construct:string
construct:for-loop
construct:for-of
construct:invocation
construct:array
construct:when-clause
construct:visibility-modifiers
construct:variable
construct:object
construct:class
construct:method
construct:implicit-conversion
construct:assignment
construct:lambda
construct:optional
construct:expression
construct:constructor
construct:method
construct:parameter
construct:invocation
construct:method
construct:property
construct:string
construct:subtract
construct:ternary
construct:unless
construct:variable
construct:visibility-modifiers
paradigm:functional
paradigm:object-oriented
technique:higher-order-functions
technique:looping
ErikSchierboom commented 8 months ago

Exercise: queen-attack

Code

class Queens 

  constructor: (options) ->
    options = options || {}
    @white = options.white || [0,3] 
    @black = options.black || [7,3]
    @board = @_drawBoard()
    @_validatePositions()

  toString: ->
    currentBoard = @board
    currentBoard.map (row, idx) ->
      formattedRow = row.join(" ")
      currentBoard[idx] = formattedRow
    currentBoard.join("\n")

  canAttack: ->
    @_sameRow() or @_sameColumn() or @_onDiagonal()

  _drawDefaultBoard: ->
    board = []
    range = [0..7]
    for row in range
      board.push([])
      for column in range
        board[row].push("O")
    board

  _drawBoard: ->
    currentBoard = @_drawDefaultBoard()
    currentBoard[@white[1]][@white[0]] = "W"
    currentBoard[@black[1]][@black[0]] = "B"
    currentBoard

  _validatePositions: ->
    throw "Queens cannot share the same space" if @_whiteBlackSamePosition()  

  _whiteBlackSamePosition: ->
    @white.join("") is @black.join("")

  _sameRow: ->
    @white[1] is @black[1]

  _sameColumn: ->
    @white[0] is @black[0]

  _onDiagonal: ->
    Math.abs(@white[1] - @black[1]) is Math.abs(@white[0] - @black[0])

module.exports = Queens

Tags:

construct:assignment
construct:class
construct:constructor
construct:for-loop
construct:if
construct:index
construct:invocation
construct:is-equality
construct:lambda
construct:logical-or
construct:method
construct:number
construct:optional-parameter
construct:parameter
construct:property
construct:string
construct:subtract
construct:throw
construct:variable
construct:visibility-modifiers
paradigm:functional
paradigm:object-oriented
technique:boolean-logic
technique:exceptions
technique:higher-order-functions
technique:looping
ErikSchierboom commented 8 months ago

Exercise: wordy

Code

class WordProblem
  constructor: (problem) ->
    @tokens = @tokenize(problem)

  answer: ->
    (new TokenizedWordProblem(@tokens)).evaluate()

  tokenize: (problem) ->
    problem = problem.slice(0, -1) # remove question mark
    problem = problem.split(" ").slice(2) # convert to array; remove "What is"
    problem = problem.filter (e) -> e != "by"
    operands = ["plus", "minus", "multiplied", "divided"]
    throw new Error("Too complicated!") unless problem.every (e, i) ->
      if i % 2 == 0
        Number(e) != NaN
      else
        e in operands
    problem

class TokenizedWordProblem
  constructor: (tokens) ->
    @tokens = tokens

  evaluate: ->
    firstNumber = Number(@tokens.shift())
    operator = @tokens.shift()
    secondNumber = Number(@tokens.shift())
    result = switch operator
      when "plus"
        firstNumber + secondNumber
      when "minus"
        firstNumber - secondNumber
      when "multiplied"
        firstNumber * secondNumber
      when "divided"
        firstNumber // secondNumber

    if @tokens.length == 0
      result
    else
      @tokens.unshift(result)
      (new TokenizedWordProblem(@tokens)).evaluate()

module.exports.WordProblem = WordProblem

Tags:

construct:add
construct:assignment
construct:class
construct:comment
construct:constructor
construct:divide
construct:error
construct:field
construct:if
construct:implicit-conversion
construct:in
construct:index
construct:invocation
construct:lambda
construct:method
construct:multiply
construct:number
construct:parameter
construct:property
construct:string
construct:subtract
construct:switch
construct:throw
construct:unless
construct:variable
construct:visibility-modifiers
paradigm:functional
paradigm:object-oriented
technique:exceptions
technique:higher-order-functions
ErikSchierboom commented 8 months ago

Exercise: beer-song

Code

class Beer

  @verse: (num) ->
    if num == 0
      lastVerse()
    else if num == 1
      singleVerse()
    else
      regularVerse(num)

  @sing: (start, finish=0) ->
    (@verse(num) for num in [start..finish]).join("\n\n") + "\n"

  regularVerse = (num) ->
    """
    #{num} bottles of beer on the wall, #{num} bottles of beer.
    Take one down and pass it around, #{num - 1} bottles of beer on the wall.
    """

  singleVerse = ->
    """
    1 bottle of beer on the wall, 1 bottle of beer.
    Take it down and pass it around, no more bottles of beer on the wall.
    """

  lastVerse = ->
    """
    No more bottles of beer on the wall, no more bottles of beer.
    Go to the store and buy some more, 99 bottles of beer on the wall.
    """

module.exports = Beer

Tags:

construct:class
construct:method
construct:string
construct:assignment
construct:class-method
construct:default-argument
construct:for-in
construct:if
construct:interpolation
construct:invocation
construct:method-overloading
construct:number
construct:parameter
construct:template-string
construct:visibility-modifiers
paradigm:object-oriented
technique:looping
ErikSchierboom commented 8 months ago

Exercise: atbash-cipher

Code

Atbash = {}

generateAlphabet = ->
  a = 'a'.charCodeAt(0)
  z = 'z'.charCodeAt(0)
  (String.fromCharCode(i) for i in [a..z])

ALPHABET = generateAlphabet()

REVERSED = do ->
  generateAlphabet().reverse()

encodeChar = (char) ->
  return char if char.match(/\d+/)
  i = ALPHABET.indexOf(char.toLowerCase())
  REVERSED[i]

Atbash.encode = (phrase) ->
  (encodeChar(c) for c in phrase)
    .join('')
    .match(/.{1,5}/g)
    .join(' ')

module.exports = Atbash

Tags:

construct:coffee-script
construct:do
construct:for-comprehension
construct:function
construct:if
construct:index
construct:invocation
construct:method
construct:module
construct:number
construct:object
construct:parameter
construct:regex
construct:return
construct:string
construct:variable
paradigm:functional
paradigm:object-oriented
technique:regular-expression
ErikSchierboom commented 8 months ago

Exercise: linked-list

Code

LinkedList = ->
  @head = null
  @count = 0

LinkedList.prototype.pushNode = (datum) ->
  return @head = new Node(datum, null, null) if !@head

  @fastForward()
  @head.next = new Node(datum, @head, null)

LinkedList.prototype.popNode = ->
  return undefined if !@head

  @fastForward()
  datum = @head.datum
  @head = @head.last
  @head.next = null
  datum

LinkedList.prototype.unshiftNode = (datum) ->
  return @head = new Node(datum, null, null) if !@head

  @rewind()
  @head.last = new Node(datum, null, @head)

LinkedList.prototype.shiftNode = () ->
  return undefined if !@head

  @rewind()
  datum = @head.datum
  @head = @head.next
  @head.last = null if @head
  datum

LinkedList.prototype.deleteNode = (datum) ->
  @rewind()
  @head = @head.next while @head.next && @head.datum != datum
  @head.next.last = @head.last if @head.next
  @head.last.next = @head.next if @head.last
  @head = @head.next || @head.last

LinkedList.prototype.countNodes = () ->
  return 0 if !@head

  count = 1
  @rewind()
  while @head.next
    count += 1
    @head = @head.next
  count

LinkedList.prototype.fastForward = ->
  @head = @head.next while @head.next

LinkedList.prototype.rewind = ->
  @head = @head.last while @head.last

Node = (@datum, @last, @next) ->

module.exports = LinkedList

Tags:

construct:assignment
construct:class
construct:constructor
construct:field
construct:if
construct:invocation
construct:logical-and
construct:logical-or
construct:method
construct:null
construct:null-coalescing
construct:number
construct:parameter
construct:prototype
construct:return
construct:short-circuiting
construct:variable
construct:visibility-modifiers
construct:while-loop
paradigm:object-oriented
technique:boolean-logic
technique:looping
ErikSchierboom commented 8 months ago

Exercise: nucleotide-count

Code

class DNA
    constructor: (seq) ->
        @nucleotideCounts = { 'A': 0, 'T': 0, 'C': 0, 'G': 0 }
        for el in seq.toUpperCase().split('')
            @nucleotideCounts[el] += 1

    count:(el) ->
        @nucleotideCounts[el] ? throw "Invalid Nucleotide"

module?.exports = DNA

Tags:

construct:assignment
construct:class
construct:constructor
construct:for-in
construct:method
construct:number
construct:object
construct:property
construct:string
construct:throw
construct:throw-expression
construct:variable
construct:visibility-modifiers
paradigm:object-oriented
technique:enumeration
technique:exceptions
technique:looping
ErikSchierboom commented 7 months ago

This is an automated comment

Hello :wave: Next week we're going to start using the tagging work people are doing on these. If you've already completed the work, thank you! If you've not, but intend to this week, that's great! If you're not going to get round to doing it, and you've not yet posted a comment letting us know, could you please do so, so that we can find other people to do it. Thanks!