Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Building a training set of tags for fsharp #1199

Closed
ErikSchierboom opened this issue Nov 1, 2023 · 16 comments
Closed

Building a training set of tags for fsharp #1199

ErikSchierboom opened this issue Nov 1, 2023 · 16 comments

Comments

@ErikSchierboom
Copy link
Member

Hello lovely maintainers 👋

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! 💙


Note: Meta discussion on the forum

@ErikSchierboom
Copy link
Member Author

Exercise: hello-world

Code

namespace ForgePlay

module HelloWorld = 

  let hello = "Hello, World!"

Tags:

construct:module
construct:namespace
construct:string
construct:let-binding
construct:variable
paradigm:functional

@ErikSchierboom
Copy link
Member Author

Exercise: collatz-conjecture

Code

module CollatzConjecture

let (|Even|Odd|) input = 
    match input % 2 with
    | 0 -> Even
    | 1 -> Odd

let steps (number: int): int option =
    let rec calculate (number: int) (count: int) =
        match number with
        | i when i <= 0 -> None
        | 1 -> Some count
        | Even -> calculate (number / 2) (count + 1)
        | Odd -> calculate (number * 3 + 1) (count + 1)
    
    calculate number 0

Tags:

construct:add
construct:divide
construct:int
construct:integral-number
construct:invocation
construct:let-binding
construct:match-expression
construct:method
construct:multiply
construct:number
construct:parameter
construct:pattern-matching
construct:recursion
construct:when-clause
paradigm:functional
technique:recursion

@ErikSchierboom
Copy link
Member Author

Exercise: armstrong-numbers

Code

module ArmstrongNumbers

open System

let round (x:float) = int (System.Math.Round x)

let isArmstrongNumber (number: int): bool =  
  let asStr = (string number)
  let lng = String.length asStr
  let sum = asStr |> 
      Seq.toList |> 
      List.map System.Char.GetNumericValue |> 
      List.sumBy (fun e -> pown e lng) |> 
      round

  sum = number  

Tags:

construct:boolean
construct:char
construct:explicit-conversion
construct:float
construct:floating-point-number
construct:functor
construct:implicit-conversion
construct:int
construct:integral-number
construct:invocation
construct:lambda
construct:list
construct:let-binding
construct:method
construct:module
construct:number
construct:open-directive
construct:parameter
construct:piping
construct:shadowing
construct:string
construct:tuple
construct:using-directive
construct:variable
paradigm:functional
paradigm:object-oriented
technique:higher-order-functions
technique:type-conversion
uses:List<T>
uses:Seq.toList

@ErikSchierboom
Copy link
Member Author

Exercise: difference-of-squares

Code

module DifferenceOfSquares

type DifferenceOfSquares(x) = 
  let range = seq { 1 .. x }
  let square x = pown x 2

  member this.squareOfSums() = 
    Seq.sum range
    |> square

  member this.sumOfSquares() =
    Seq.map square range 
    |> Seq.sum

  member this.difference() = 
    this.squareOfSums() - this.sumOfSquares()

Tags:

construct:class
construct:constructor
construct:invocation
construct:let-binding
construct:member
construct:method
construct:parameter
construct:subtract
construct:type
construct:using-directive
construct:visibility-modifiers
paradigm:object-oriented

@ErikSchierboom
Copy link
Member Author

Exercise: pangram

Code

module Pangram

let isPangram (input: string): bool = failwith "You need to implement this function."

let isPangram (input: string): bool =
    let alphabetList = 'a'..'z'

    let mutable duplicateCheck = []

    let mutable counter = 0

    for letter in input do
        if alphabetList |> list.contains true && alphabetList |> duplicateCheck.contains false then
            duplicateCheck += letter
            counter += 1
            
            

Tags:

construct:boolean
construct:char
construct:double
construct:floating-point-number
construct:for-loop
construct:if-then-else
construct:implicit-conversion
construct:int
construct:integral-number
construct:invocation
construct:let-binding
construct:list
construct:method
construct:module
construct:number
construct:parameter
construct:string
construct:throw
construct:using-directive
construct:variable
construct:visibility-modifiers
paradigm:functional
paradigm:imperative
paradigm:object-oriented
technique:exceptions
technique:looping
uses:List<T>

@ErikSchierboom
Copy link
Member Author

Exercise: meetup

Code

module Meetup
open System

type Schedule = First | Second | Third | Fourth | Last | Teenth

let meetupDay (dayOfWeek: DayOfWeek) schedule year month =
    let d = if schedule = Teenth then DateTime(year, month, 13)
            elif schedule = Last then DateTime(year, month, DateTime.DaysInMonth(year, month) - 6)
            else DateTime(year, month, 1)
    let first = int d.DayOfWeek
    let dayNum = int dayOfWeek
    let firstGivenDay = if first < dayNum then d.AddDays (float dayNum - float first)
                        elif first > dayNum then d.AddDays (7. - float first + float dayOfWeek)
                        else d
    match schedule with
    | First | Teenth | Last -> firstGivenDay
    | Second -> firstGivenDay.AddDays 7.
    | Third -> firstGivenDay.AddDays 14.
    | Fourth -> firstGivenDay.AddDays 21.

Tags:

construct:add
construct:big-float
construct:date-time
construct:double
construct:floating-point-number
construct:if-elif-else
construct:implicit-conversion
construct:int
construct:integral-number
construct:invocation
construct:lambda
construct:method
construct:module
construct:number
construct:pattern-matching
construct:subtract
construct:type
construct:using-directive
construct:visibility-modifiers
paradigm:functional
paradigm:object-oriented
technique:higher-order-functions
uses:DateTime

@ErikSchierboom
Copy link
Member Author

Exercise: pythagorean-triplet

Code

module PythagoreanTriplet

let triplet x y z = 
    [x; y; z]
    |> List.sort
    |> function
       | [p; q; r] -> (p, q, r)
       | _ -> failwith ""

let isPythagorean (x, y, z) =
    x*x + y*y = z*z

let N = Seq.initInfinite id

let pairs =
    seq {for t in N do for x in [0..t] do yield (x, t - x)}

let triplets =
    seq {
        for (m, n) in pairs do
            let a = m*m - n*n
            let b = 2*m*n
            let c = m*m + n*n
            if a > 0 && b > 0 && c > 0
            then yield triplet a b c
    }

let pythagoreanTriplets low high =
    triplets
    |> Seq.skipWhile (fun (a, b, c) -> a < low || b < low || c < low)
    |> Seq.takeWhile (fun (a, b, c) -> a <= high && b <= high && c <= high)
    |> List.ofSeq

Tags:

construct:add
construct:boolean
construct:do
construct:f#
construct:for-loop
construct:function
construct:if-then-else
construct:implicit-conversion
construct:int
construct:integral-number
construct:invocation
construct:lambda
construct:let-binding
construct:list
construct:logical-and
construct:logical-or
construct:method
construct:multiply
construct:number
construct:parameter
construct:pattern-matching
construct:seq
construct:sequence-expression
construct:subtract
construct:tuple
construct:underscore
construct:using-directive
construct:variable
construct:visibility-modifiers
paradigm:functional
paradigm:imperative
paradigm:object-oriented
technique:boolean-logic
technique:higher-order-functions
technique:looping
uses:List<T>
uses:Tuple

@ErikSchierboom
Copy link
Member Author

Exercise: prime-factors

Code

module PrimeFactors

let primes = 
    let rec primesRec candidates =
        seq {
            let nextPrime = candidates |> Seq.head
            yield int64 nextPrime

            let remainingCandidates = 
                candidates 
                |> Seq.tail
                |> Seq.filter (fun el -> el % nextPrime <> 0)   
            yield! primesRec remainingCandidates
        }
    let initialCandidates = Seq.initInfinite <| (+) 2
    primesRec initialCandidates

let factorsFor factors number =
    let rec f acc number =
        if number =1L then 
            acc |> List.rev
        else
            let factor =
                factors
                |> Seq.find (fun p -> number % p = 0L)
            f (factor :: acc) (number / factor) 
    f [] number

let primeFactorsFor = factorsFor primes

Tags:

construct:add
construct:divide
construct:equals
construct:if
construct:ignore
construct:int64
construct:invocation
construct:lambda
construct:list
construct:long
construct:method
construct:number
construct:parameter
construct:pattern-matching
construct:recursion
construct:seq
construct:sequence-expression
construct:seqhead
construct:seqtail
construct:seqyield
construct:using-directive
construct:yield
paradigm:functional
paradigm:object-oriented
technique:higher-order-functions
technique:laziness
technique:looping
technique:recursion
uses:List<'a>

@ErikSchierboom
Copy link
Member Author

Exercise: roman-numerals

Code

namespace RomanNumeral

type RomanNumeral() = 
    let numbers = 
        Map.empty
            .Add(1000, "M")
            .Add(900, "CM")
            .Add(500, "D")
            .Add(400, "CD")
            .Add(100, "C")
            .Add(90, "XC")
            .Add(50, "L")
            .Add(40, "XL")
            .Add(10, "X")
            .Add(9, "IX")
            .Add(5, "V")
            .Add(4, "IV")
            .Add(1, "I")
    let keys = numbers |> Map.toSeq |> Seq.map fst |> Seq.sortBy (fun x -> -x - 1) |> Seq.toList

    member this.toRoman(value: int) =
        this.addNumerals(value, 0)

    member private this.addNumerals(value: int, keyidx: int) =
        if (keyidx >= keys.Length)
        then ""
        else if (value >= keys.[keyidx])
             then (numbers.[keys.[keyidx]] + this.addNumerals(value - keys.[keyidx], keyidx))
             else this.addNumerals(value, keyidx + 1)

Tags:

construct:add
construct:class
construct:if
construct:implicit-conversion
construct:indexer
construct:int
construct:integral-number
construct:invocation
construct:lambda
construct:let
construct:local-function
construct:map
construct:member-overloading
construct:method
construct:number
construct:parameter
construct:recursion
construct:string
construct:subtract
construct:then
construct:type
construct:using-directive
construct:variable
construct:visibility-modifiers
paradigm:functional
paradigm:object-oriented
technique:higher-order-functions
technique:recursion
uses:Map<'TKey,'TValue>

@ErikSchierboom
Copy link
Member Author

Exercise: circular-buffer

Code

module CircularBuffer

type CircularBuffer<'a> = private {items: 'a option []; nextIn: int; nextOut: int}

module private Internal =
    type Write = Normal | Forced

    let nextIndex i buffer = (i + 1) % buffer.items.Length

    let write x buffer writeType =
        let newItems = Array.copy buffer.items

        let newNextOut =
            match (writeType, newItems.[buffer.nextIn]) with
            | (_, None)        -> buffer.nextOut
            | (Forced, Some _) -> nextIndex buffer.nextOut buffer
            | (Normal, Some _) -> failwith "Buffer full"

        newItems.[buffer.nextIn] <- Some x

        { items = newItems
          nextIn = nextIndex buffer.nextIn buffer
          nextOut = newNextOut }

open Internal

let mkCircularBuffer size =
    { items = Array.init size (fun _ -> None)
      nextIn = 0
      nextOut = 0 }

let write x buffer = Internal.write x buffer Normal

let read buffer =
    let newItems = Array.copy buffer.items
    let outValue = buffer.items.[buffer.nextOut].Value
    newItems.[buffer.nextOut] <- None

    let newBuffer =
        { buffer with
            items = newItems
            nextOut = nextIndex buffer.nextOut buffer }

    (outValue, newBuffer)

let clear buffer = mkCircularBuffer buffer.items.Length

let forceWrite x buffer = Internal.write x buffer Forced

Tags:

construct:add
construct:assignment
construct:attribute
construct:constructor
construct:field
construct:floating-point-number
construct:generic-type
construct:impl
construct:indexer
construct:int
construct:integral-number
construct:invocation
construct:lambda
construct:let
construct:match
construct:module
construct:named-argument
construct:number
construct:open
construct:parameter
construct:pattern-matching
construct:record
construct:subtract
construct:sum-type
construct:tuple
construct:type
construct:union
construct:visibility-modifiers
paradigm:functional
paradigm:imperative
paradigm:object-oriented
technique:higher-order-functions

@ErikSchierboom
Copy link
Member Author

Exercise: bank-account

Code

module BankAccount

type BankAccount() = 
    
    let mutable (balance : float option) = None
    member t.Balance = balance

    member t.openAccount() = 
        balance <- Some(0.0)

    member t.updateBalance(x : float) = 
        if balance.IsSome then balance <- Some(balance.Value + x)
        else failwith("account closed")

    member t.getBalance() = balance.Value |> decimal

    member t.closeAccount() = 
        balance <- None

Tags:

construct:add
construct:assignment
construct:decimal
construct:float
construct:floating-point-number
construct:if
construct:implicit-conversion
construct:int
construct:integral-number
construct:invocation
construct:lambda
construct:let-binding
construct:method
construct:mutable-field
construct:number
construct:optional-parameter
construct:parameter
construct:pipe-forward
construct:property
construct:type
construct:visibility-modifiers
paradigm:functional
paradigm:imperative
paradigm:object-oriented
technique:higher-order-functions
uses:floats

@ErikSchierboom
Copy link
Member Author

Exercise: complex-numbers

Code

module ComplexNumbers

let create real imaginary = Complex(real, imaginary)

let mul z1 z2 = Complex.Multiply(z1, z2)

let add z1 z2 = Complex.Add(z1, z2)

let sub z1 z2 = Complex.Subtract(z1, z2)

let div z1 z2 = Complex.Divide(z1, z2)

let abs z = Complex.Abs z

let conjugate z = Complex.Conjugate z

let real (z: Complex) = z.Real

let imaginary (z: Complex) = z.Imaginary

let exp z = Complex.Exp z

Tags:

construct:complex-number
construct:fsharp-unicode-identifier
construct:invocation
construct:let-binding
construct:module
construct:number
construct:parameter
construct:tuple
construct:visibility-modifiers
paradigm:functional
paradigm:object-oriented

@ErikSchierboom
Copy link
Member Author

Exercise: rail-fence-cipher

Code

module RailFenceCipher

open System

let private getPositions n length =
    Seq.init length (fun index ->
        let offset = ((index - 1) % (n - 1)) + 1
        match index with
        | 0 -> (0, 0)
        | _ -> match ((index - 1) / (n - 1)) % 2 with
                | 0 -> (index, offset)
                | _ -> (index, n - 1 - offset)
    )

let encode n input =
    Seq.zip
        ((getPositions n (Seq.length input)) |> Seq.map snd)
        input
    |> Seq.groupBy fst
    |> Seq.map snd
    |> Seq.map (fun row ->
        row
        |> Seq.map snd
        |> Seq.fold (sprintf "%s%c") String.Empty
    )
    |> Seq.fold (sprintf "%s%s") String.Empty

let decode n input =
    let tmp =
        getPositions n (Seq.length input)
        |> Seq.groupBy snd
        |> Seq.map snd
        |> Seq.concat
        |> Seq.map fst

    Seq.zip
        tmp
        input
    |> Seq.sort
    |> Seq.map snd
    |> Seq.fold (sprintf "%s%c") String.Empty

Tags:

construct:add
construct:char
construct:divide
construct:floating-point-number
construct:functor
construct:implicit-conversion
construct:int
construct:integral-number
construct:invocation
construct:lambda
construct:let-binding
construct:match-expression
construct:method
construct:number
construct:operator-overloading
construct:parameter
construct:pattern-matching
construct:private
construct:sequence
construct:subtract
construct:tuple
construct:underscore
construct:using-directive
construct:visibility-modifiers
paradigm:functional
technique:higher-order-functions
uses:ValueTuple

@ErikSchierboom
Copy link
Member Author

Exercise: pov

Code

module Pov

type Graph<'a> = 
    | Graph of 'a * 'a Graph List
    member x.children = let (Graph(_, xs)) = x in xs
    member x.value = let (Graph(v, _)) = x in v
    override x.ToString() = 
        match x with
        | Graph(v,[]) -> v.ToString()
        | Graph(v,xs) -> sprintf "(%O,%O)" v xs

type Path<'a> = 
    | Top
    | Node of 'a * 'a Graph List * 'a Path * 'a Graph List
    override x.ToString() = 
        match x with
        | Top -> "T"
        | Node(v,xs,p,ys) -> sprintf "(%O,L%O,%O,R%O)" v xs p ys

type Location<'a> = 
    Loc of 'a Graph option * 'a Path

let left (Loc(t,p)) = 
    match t with
    | None -> failwith "left of none"
    | Some(g) -> 
        match p with
        | Top -> failwith "left of top"
        | Node(v,l::left,up,right) -> Loc(Some(l),Node(v,left,up,g::right))
        | Node(v,[],up,right) -> failwith "left of first"

let right (Loc(t,p)) = 
    match t with
    | None -> failwith "left of none"
    | Some(g) -> 
        match p with
        | Top -> failwith "right of top"
        | Node(v,left,up,r::right) -> Loc(Some(r),Node(v,g::left,up,right))
        | _ -> failwith "right of last"

let up (Loc(t,p)) = 
    match p with
    | Top -> failwith "up of top"
    | Node(v,left,up,right) -> 
        let rr = 
            match t with
            | None -> right
            | Some(x) -> x::right
        Loc(Some(Graph(v, (left |> List.rev) @ (rr))),up)

let down (Loc(t,p)) = 
    match t with
    | None -> failwith "down of none"
    | Some(Graph(_,[])) -> failwith "down of item"
    | Some(Graph(v,t1::graphs)) -> Loc(Some(t1),Node(v,[],p,graphs))

let rec toGraph lc = 
    match lc with
    | Loc(gr, Top) -> gr
    | lc -> toGraph (up lc)

let fromGraph gr = Loc(Some(gr), Top)

let mkGraph v xs = Graph(v,xs)

let rec search v lc = 
    match lc with
    | Loc(None,_) -> None
    | Loc(Some(Graph(x,_)),_) when v = x -> Some(lc)
    | Loc(Some(Graph(x,xs)),Top) -> 
        match xs with
        | [] -> None
        | _ ->
            //printfn "search down from top"
            lc |> down |> search v
    | Loc(Some(Graph(x,xs)),p) -> 
        match xs with
        | [] -> 
            match p with
            | Node(_,_,_,[]) -> None
            | _ -> 
                //printfn "search right from %O" x
                lc |> right |> search v
        | _ ->
            let downFounded = 
                //printfn "search down from %O" x
                lc |> down |> search v
            match downFounded with
            | Some(_) -> downFounded
            | None -> 
                match p with
                | Node(_,_,_,[]) -> None
                | _ -> 
                    //printfn "search right from %O" x
                    lc |> right |> search v

let delete (Loc(x,p)) = 
    match p with
    | Top -> failwith "delete of top"
    | Node(v,left,up,r::right) -> Loc(Some(r),Node(v,left,up,right))
    | Node(v,l::left,up,[]) -> Loc(Some(l),Node(v,left,up,[]))
    | Node(v,[],up,[]) -> Loc(None, p)

let rec fromPOVLoc lc = 
    match lc with
    | Loc(None,_) -> []
    | Loc(Some(gr),Top) -> [gr]
    | Loc(Some(Graph(x,children)),_) -> 
        let upperPOV = lc |> delete |> up |> fromPOVLoc
        [Graph(x, children@upperPOV)]

let rec fromPOV v tree = 
    let founded = 
        tree
        |> fromGraph
        |> search v
    match founded with
    | None -> None
    | Some(lc) -> 
        lc
        |> fromPOVLoc
        |> List.head
        |> Option.Some

let trace p tree = 
    let rec findInner acc t =
        match t with
        | Graph(n, _) when p(n) -> Some(acc@[n])
        | Graph(x, children) -> children |> Seq.choose (findInner (acc@[x])) 
                                          |> Seq.tryFind (fun _ -> true)
        | Graph(_, []) -> Some([])
    findInner [] tree

let tracePathBetween v1 v2 tree = 
    let pov = tree |> fromPOV v1
    match pov with
    | None -> None
    | Some(pv) -> pv |> trace (fun x -> x = v2)

Tags:

construct:active-pattern
construct:annotation
construct:attribute
construct:class
construct:comment
construct:constructor
construct:generic-type
construct:fsharp-type-abbreviation
construct:implicit-conversion
construct:invocation
construct:lambda
construct:list
construct:let-binding
construct:match-expression
construct:method
construct:module
construct:overloading
construct:parameter
construct:pattern-matching
construct:recursion
construct:sequence-expression
construct:string
construct:throw
construct:tuple
construct:type-constructor
construct:union
construct:using-directive
construct:visibility-modifiers
paradigm:functional
paradigm:object-oriented
technique:exceptions
technique:higher-order-functions
technique:recursion
uses:List<T>
uses:Seq

@ErikSchierboom
Copy link
Member Author

Exercise: dnd-character

Code

module DndCharacter

open System

let modifier (x : int) =
    float(x - 10) / 2.0
    |> Math.Floor
    |> int

let ability() = 
    let seed = Random()
    let d6 = seed.Next(1,6)
    [ d6; d6 ; d6 ; d6 ]
    |> List.sortDescending
    |> List.take 3
    |> List.sum

type DndCharacter() =
    let (str, dex, con, wis, intel, chr) = 
        (ability(), ability(), ability(), ability(), ability(), ability())

    member __.Strength with get() = str
    member __.Dexterity with get() = dex
    member __.Constitution with get() = con
    member __.Intelligence with get() = intel
    member __.Wisdom with get() = wis
    member __.Charisma with get() = chr
    member __.Hitpoints with get() = 10 + modifier(__.Constitution)

Tags:

construct:add
construct:assignment
construct:class
construct:divide
construct:explicit-conversion
construct:float
construct:floating-point-number
construct:getter
construct:implicit-conversion
construct:int
construct:integral-number
construct:invocation
construct:lambda
construct:let
construct:list
construct:local-function
construct:method
construct:module
construct:number
construct:parameter
construct:property
construct:tuple
construct:type-conversion
construct:using-directive
construct:variable
construct:visibility-modifiers
paradigm:functional
paradigm:imperative
paradigm:object-oriented
technique:higher-order-functions
uses:List<'t>
uses:Random
uses:Tuple

@ErikSchierboom
Copy link
Member Author

This is an automated comment

Hello 👋 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!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant