Project: Part 2,Solution

$30.00 $24.00

Since working on part 1 of the project, you’ve learned many approaches that will allow us to improve both the design of data/functions, as well as add new functionality! == Data/Function Design == – You’ll enhance each flash card to support an arbitrary number of “tags” (i.e., string labels). – You’ll generalize the meaning of…

Rate this product

You’ll get a: zip file solution

 

Categorys:

Description

Rate this product

Since working on part 1 of the project, you’ve learned many

approaches that will allow us to improve both the design of

data/functions, as well as add new functionality!

== Data/Function Design ==

– You’ll enhance each flash card to support an arbitrary

number of “tags” (i.e., string labels).

– You’ll generalize the meaning of a deck, such as to be

agnostic as to the very meaning of cards (and thus

support a wider variety of decks).

– You’ll enhance the menu system to be re-usable, as

well as to support quitting (i.e., leave without forcing a

selection).

== Application Features ==

– You’ll implement a second method for interpreting

self-reported correctness of a card, this time using

some machine learning (ML) to process natural language (NLP);

the user will be able to select which method to use (since

both methods have their tradeoffs!).

– When a user doesn’t get a card correct (via self-report),

that card is placed at the back of the deck; thus, a deck

is only completed when a user gets all cards correct.

– You’ll provide deck options that are a subset of cards

containing a particular tag (e.g., all “hard” cards, or

those in the topic of “science”).

– Once the program is run, the user will be able to study

as many decks as they wish, selecting subsequent decks

from the menu until they quit.

Of course, we’ll design this program step-by-step 🙂

When designing this enhanced project, you are welcome to draw

upon your project part 1, our sample solutions (for part 1, and

any homework), and/or lecture notes as you see fit & helpful.

Lastly, here are a few overall project requirements…

– Now that mutation has been covered, you may use it (unless

otherwise stated in the instructions); however, your usage

will be evaluated based upon the guidelines from class.

– As included in the instructions, all interactive parts of

this program MUST make effective use of the reactConsole

framework.

– Staying consistent with our Style Guide…

* All functions must have:

a) a preceding comment specifying what it does

b) an associated @EnabledTest function with sufficient

tests using testSame

* All data must have:

a) a preceding comment specifying what it represents

b) associated representative examples

c) for classes with member functions, an associated

@EnabledTest function with sufficient tests for all

the member functions of the class

– You will be evaluated on a number of criteria, including…

* Adherence to instructions and the Style Guide

* Correctly producing the functionality of the program

* Design decisions that include choice of tests, appropriate

application of programming approaches (e.g., sequence

abstractions, recursion, mutation), and task/type-driven

decomposition of functions.

—————————————————————–

Flash Card data design

(Hint: see Homework 5, Problem 3)

—————————————————————–

TODO 1/1: Design the data type TaggedFlashCard to represent a

single flash card.

You should be able to represent the text prompt on

the front of the card, the text answer on the back,

as well as any number of textual tags (such as “hard”

or “science” — this shouldn’t come from any fixed

set of options, but truly open to however someone

wishes to categorize their cards).

Each card should have two member functions:

– isTagged, which determines if the card has a

supplied tag (e.g., has this card been tagged

as “hard”?)

– fileFormat, which produces a textual representation

of the card as “front|back|tag1,tag2,…”; that is

all three parts of the card separated with the pipe

(‘|’) character, and further separate any tags with

a comma

Include *at least* 3 example cards (which will come

in handy later for tests!), and make sure to test

the required member functions.

(just useful values for

the separation characters)

val sepCard = “|”

val sepTag = “,”

—————————————————————–

Files of tagged flash cards

—————————————————————–

Now that we have our updated cards, let’s update how we read

them from files.

TODO 1/2: Design the function stringToTaggedFlashCard that

takes a string, assumed to be in the format described

for the fileFormat member function above, and produces

the corresponding tagged flash card.

Hint: review part 1 of the project, TODO 2/3

TODO 2/2: Design the function readTaggedFlashCardsFile that

takes a path to a file and produces a list of

tagged flash cards.

If the file does not exist, return an empty list.

Otherwise, you can assume that every line is

formatted in the string format we just worked with.

Hint:

– Review part 1 of the project, TODO 3/3

– We’ve provided an “example_tagged.txt” file that you

can use for testing if you’d like; also make sure to

test your function when the supplied file does not

exist!

—————————————————————–

Deck design

—————————————————————–

If you think about it, once a deck has been selected, our study

application doesn’t need much information about cards to work…

in fact, it doesn’t even need the concept of a card. Consider

the following:

The deck is either exhausted,

showing the question, or

showing the answer

enum class DeckState {

EXHAUSTED,

QUESTION,

ANSWER,

}

Basic functionality of any deck

interface IDeck {

The state of the deck

fun getState(): DeckState

The currently visible text

(or null if exhausted)

fun getText(): String?

The number of question/answer pairs

(does not change when question are

cycled to the end of the deck)

fun getSize(): Int

Shifts from question -> answer

(if not QUESTION state, returns the same IDeck)

fun flip(): IDeck

Shifts from answer -> next question (or exhaustion);

if the current question was correct it is discarded,

otherwise cycled to the end of the deck

(if not ANSWER state, returns the same IDeck)

fun next(correct: Boolean): IDeck

}

This contract of operations will allow our study application to

work with a variety of sources, including lists and even code

that never explicitly stores cards!

(For a similar problem, see Homework 6, Problem 3, TODO 2,

where you implemented stateful classes to integrate with an

object-oriented reactConsole.)

TODO 1/2: Design TFCListDeck to implement the IDeck interface

for a supplied list of tagged flash cards. For this

problem your class must have *no* mutable state and

all member data should be private.

When testing, make sure to test the behavior of all

the member functions of the interface in a variety

of situations.

Hint: using default arguments can make your class

easier to create initially, see…

kotlinlang.org/docs/functions.html#default-arguments

TODO 2/2: Now design PerfectSquaresDeck to implement the IDeck

interface. You are *not* allowed to generate any

flash cards, nor have mutable state; the goal is to

act as though it had a list produced by the

perfectSquares function in part 1 of the project,

but without ever having to generate all those cards!

Again, as is generally good practice, keep all your

member data private!

Hint: you will still need to keep track of the

*sequence* of upcoming numbers (particularly

as some may get cycled back due to incorrect

responses).

—————————————————————–

Menu design

—————————————————————–

The chooseOption function in part 1 of the project was good, but

let’s see what we can do to improve upon it in two core ways…

a) Part 1 allowed you to select from amongst decks, which means

you’d have to copy-paste if you wanted to have a menu of

other data (such as files, or months of the year); let’s

make the function agnostic as to the type of the list items

being selected.

b) Part 1 didn’t allow for the possibility of not selecting an

option; let’s add a quit feature!

To help with (a), consider the following interface, which

requires that a menu option be able to return a textual

representation (that is then displayed in the menu!)…

the only required capability for a menu option

is to be able to render a title

interface IMenuOption {

fun menuTitle(): String

}

as well as the following general implementation (great for

tests & examples), which satisfies the contract via pairing

a value (of any type) with a name…

a menu option with a single value and name

data class NamedMenuOption<T>(val option: T, val name: String) : IMenuOption {

override fun menuTitle(): String = name

}

individual examples, as well as a list

(an example for a list of menu options!)

val opt1A = NamedMenuOption(1, “apple”)

val opt2B = NamedMenuOption(2, “banana”)

val optsExample = listOf(opt1A, opt2B)

TODO 1/1: Finish designing the program chooseMenuOption that

takes a list (assumed to be non-empty) of any type

(as long as it implements the IMenuOption interface),

produces a corresponding numbered menu (1-# of list

items, each showing its menuTitle), and returns the

list item corresponding to the number entered (or null

if 0 was entered to indicate a desire to quit without

choosing an option). Keep displaying the menu until a

valid menu selection (or quitting) is indicated.

Hints:

– You’ll find the code from chooseOption (in part 1)

to be a *very* good starting point.

– Homework 5, Problem 4, has a very similar interface,

which can give you an idea for how you’d use it.

– To help you get started, you have some examples

above and prompts below; a “stub” for the

chooseMenuOption function (to help with the

signature and overall structure); and a set of

tests that should pass once the program has been

completed.

Some useful outputs

val menuPrompt = “Enter your choice (or 0 to quit)”

val menuQuit = “You quit”

val menuChoicePrefix = “You chose: “

Provides an interactive opportunity for the user to choose

an option or quit.

fun <T : IMenuOption> chooseMenuOption(options: List<T>): T? {

your code here!

– call reactConsole (with appropriate handlers)

– return the selected option (or null for quit)

}

@EnabledTest

fun testChooseMenuOption() {

testSame(

captureResults(

{ chooseMenuOption(listOf(opt1A)) },

“howdy”,

“0”,

),

CapturedResult(

null,

“1. ${opt1A.name}”,

“”,

menuPrompt,

“1. ${opt1A.name}”,

“”,

menuPrompt,

menuQuit,

),

“quit”,

)

testSame(

captureResults(

{ chooseMenuOption(optsExample) },

“hello”,

“10”,

“-3”,

“1”,

),

CapturedResult(

opt1A,

“1. ${opt1A.name}”, “2. ${opt2B.name}”, “”, menuPrompt,

“1. ${opt1A.name}”, “2. ${opt2B.name}”, “”, menuPrompt,

“1. ${opt1A.name}”, “2. ${opt2B.name}”, “”, menuPrompt,

“1. ${opt1A.name}”, “2. ${opt2B.name}”, “”, menuPrompt,

“${menuChoicePrefix}${opt1A.name}”,

),

“1”,

)

testSame(

captureResults(

{ chooseMenuOption(optsExample) },

“3”,

“-1”,

“2”,

),

CapturedResult(

opt2B,

“1. ${opt1A.name}”, “2. ${opt2B.name}”, “”, menuPrompt,

“1. ${opt1A.name}”, “2. ${opt2B.name}”, “”, menuPrompt,

“1. ${opt1A.name}”, “2. ${opt2B.name}”, “”, menuPrompt,

“${menuChoicePrefix}${opt2B.name}”,

),

“2”,

)

}

—————————————————————–

Machine learning for sentiment analysis

—————————————————————–

In part 1 of the project, you designed isPositive as a way to

interpret whether a student’s self-report was positive or

negative; in the world of Machine Learning (a subfield of

Artificial Intelligence, or AI), this is an approach to

“sentiment analysis” – a problem in Natural Language Processing

(NLP) that seeks to analyze text to understand the emotional

tone of some text.

In this context, what you built was a “binary classifier” of

text, meaning it output one of two values according to the input

string. In Kotlin we can describe this input-output relationship

using the following shortcut…

typealias PositivityClassifier = (String) -> Boolean

This code simply means we can now use PositivityClassifier

anywhere we would have used the type on the right (e.g.,

as the type in a function’s parameter or return type).

Our goal is now to try and use a more sophisticated approach

to sentiment analysis – one that learns positivity/negativity

based upon a dataset of supplied examples. To represent such a

dataset, consider the following type…

data class LabeledExample<E, L>(val example: E, val label: L)

This associates a “label” (such as positive vs negative, or

cat video vs boring) with an example. Here is one such dataset:

val datasetYN: List<LabeledExample<String, Boolean>> =

listOf(

LabeledExample(“yes”, true),

LabeledExample(“y”, true),

LabeledExample(“indeed”, true),

LabeledExample(“aye”, true),

LabeledExample(“oh yes”, true),

LabeledExample(“affirmative”, true),

LabeledExample(“roger”, true),

LabeledExample(“uh huh”, true),

LabeledExample(“true”, true),

just a visual separation of

the positive/negative examples

LabeledExample(“no”, false),

LabeledExample(“n”, false),

LabeledExample(“nope”, false),

LabeledExample(“negative”, false),

LabeledExample(“nay”, false),

LabeledExample(“negatory”, false),

LabeledExample(“uh uh”, false),

LabeledExample(“absolutely not”, false),

LabeledExample(“false”, false),

)

FYI: we call this dataset “balanced” since it has an equal

number of examples of the labels (i.e., # true and #false).

Such a balance is *one* tool (of many) when trying to avoid

algorithmic bias (en.wikipedia.org/wiki/Algorithmic_bias).

Notice that our simple heuristic of the first letter is pretty

good according to this dataset, but will make some lucky

guesses (e.g., “false”) and some actual mistakes (e.g., “true”).

We have provided below that code, as well as a set of tests that

reference our labeled dataset – make sure you understand all of

this code (including the comments in the tests about when & how

the heuristic is predictably getting the answer wrong).

Heuristically determines if the supplied string

is positive based upon the first letter being Y

fun isPositiveSimple(s: String): Boolean {

return s.uppercase().startsWith(“Y”)

}

tests that an element of the dataset matches

with expectation of its correctness on a

particular classifier

fun helpTestElement(

index: Int,

expectedIsCorrect: Boolean,

isPos: PositivityClassifier,

) {

testSame(

isPos(datasetYN[index].example),

when (expectedIsCorrect) {

true -> datasetYN[index].label

false -> !datasetYN[index].label

},

when (expectedIsCorrect) {

true -> datasetYN[index].example

false -> “${ datasetYN[index].example } <- WRONG”

},

)

}

@EnabledTest

fun testIsPositiveSimple() {

val classifier = ::isPositiveSimple

correctly responds with positive

for (i in 0..1) {

helpTestElement(i, true, classifier)

}

incorrectly responds with negative

for (i in 2..8) {

helpTestElement(i, false, classifier)

}

correctly responds with negative, sometimes

due to luck (i.e., anything not starting

with the letter Y is assumed negative)

for (i in 9..17) {

helpTestElement(i, true, classifier)

}

}

One approach we *could* take is just to have the computer learn

by rote memorization: that is, respond with the labeled answer

from the dataset. But what about if the student supplies an

input not in this list? The approach we’ll try as a way to

handle this situation is the following…

– If the response is known in the dataset (independent of

upper/lower-case), use the associated label

– Otherwise…

Find the 3 “closest” examples and respond with a majority

vote of their associated labels

This algorithm will represent our attempt to “generalize”

from the dataset; we know we’ll always get certain responses

correct, and we’ll let our dataset inform the response of

unknown inputs. As with all approaches based upon machine

learning, this approach is likely to make mistakes (even those

that we’ll find confusing/comical), and so we should be

judicious in how we apply the system in the world.

Now let’s build up this classifier, step-by-step 🙂

TODO 1/5: When finding closest examples, and majority vote, it

will be helpful to be able to get the “top-k” of a

list by some measure; meaning, a function that can

get the top-3 strings in a list by length, but

equally identify the top-1 (i.e., best) song by

ratings. To help, consider the following definition

of an “evaluation” function: one that takes an input

of some type and associates an output “score” (where

bigger scores are understood to be better):

typealias EvaluationFunction<T> = (T) -> Int

Design the function topK that takes a list of

items, k (assumed to be a postive integer), and a

corresponding evaluation function, and then returns

the k items in the list that get the highest score

(if there are ties, you are free to return any of the

winners; if there aren’t enough items in the list,

return as many as you can).

Hint: You did this problem in Homework 7, Problem 1

– To simplify, you can avoid the ItemScore type

by using the built-in `zip` function that you

implemented in Homework 7, Problem 3.

– Later functions will use topK and assume the

parameter ordering is as described above (which

is a small swap from the sample solution).

TODO 2/5: Great! Now we have to answer the question from before:

what does it mean for two strings to be “close”?

There are actually multiple reasonable ways of

capturing such a distance, one of which is the

Levenshtein Distance, which describes the minimum

number of single-character changes (e.g., adding a

character, removing one, or substituting) required to

change one sequence into another

(https: en.wikipedia.org/wiki/Levenshtein_distance).

Your task is to design the function

levenshteinDistance that computes this distance for

two supplied strings.

Hint: Homework 7, Problem 2 🙂

TODO 3/5: Great! Now let’s design a “k-Nearest Neighbor”

classifier (you can read online description, such as

on Wikipedia, for lots of details & variants, but

we’ll give you all the information you need here).

The goal here: given a dataset of labeled examples,

a distance function, and a number k, let the k

closest elements of the dataset “vote” (with their

label) as to what the label of a new element

should be. To be clear, here is a way of describing

a distance function, producing a integer distance

between two elements of a type…

typealias DistanceFunction<T> = (T, T) -> Int

Since this method might give an incorrect response,

we’ll return not only predicted label, but the number

of “votes” received for that label (out of k)…

data class ResultWithVotes<L>(val label: L, val votes: Int)

Your task is to uncomment and then *test* the supplied

nnLabel function (note: you might need to fix up the

ordering of your topK arguments to play nicely with

the code here – you should NOT change this function).

You’ll find guiding comments to help.

uses k-nearest-neighbor (kNN) to predict the label

for a supplied example given a labeled dataset

and distance function

fun <E, L> nnLabel(

queryExample: E,

dataset: List<LabeledExample<E, L>>,

distFunc: DistanceFunction<E>,

k: Int,

): ResultWithVotes<L> {

1. Use topK to find the k-closest dataset elements:

finding the elements whose negated distance is the

greatest is the same as finding those that are closest.

val closestK =

topK(dataset, k) {

-distFunc(queryExample, it.example)

}

2. Discard the examples, we only care about their labels

val closestKLabels = closestK.map { it.label }

3. For each distinct label, count up how many time it

showed up in step #2

(Note: once we know the Map type, there are WAY simpler

ways to do this!)

val labelsWithCounts =

closestKLabels.distinct().map {

label ->

Pair(

first = label

label,

second = number of votes

closestKLabels.filter({ it == label }).size,

)

}

4. Use topK to get the label with the greatest count

val topLabelWithCount = topK(labelsWithCounts, 1, { it.second })[0]

5. Return both the label and the number of votes (of k)

return ResultWithVotes(

topLabelWithCount.first,

topLabelWithCount.second,

)

}

@EnabledTest

fun testNNLabel() {

don’t change this dataset:

think of them as points on a line…

(with ? referring to the example below)

a a ? b b

|— — — — — — — — — —|

1 2 3 4 5 6 7 8 9 10

val dataset =

listOf(

LabeledExample(2, “a”),

LabeledExample(3, “a”),

LabeledExample(7, “b”),

LabeledExample(10, “b”),

)

A simple distance: just the absolute value

fun myAbsVal(

a: Int,

b: Int,

): Int {

val diff = a – b

return when (diff >= 0) {

true -> diff

false -> -diff

}

}

TODO: to demonstrate that you understand how kNN is

supposed to work (and what the supplied code returns),

you are going to write tests here for a selection of

cases that use the dataset and distance function above.

To help you get started, consider testing for point 5,

with k=3:

a) All the points with their distances are…

a = |2 – 5| = 3

a = |3 – 5| = 3

b = |7 – 5| = 2

b = |10 – 5| = 5

b) SO, the labels of the three closest are…

a (2 votes)

b (1 vote)

c) SO, kNN in this situation would predict the label

for this point to be “a”, with confidence 2/3 (medium)

We capture this test as…

testSame(

nnLabel(5, dataset, ::myAbsVal, k = 3),

ResultWithVotes(“a”, 2),

“NN: 5->a, 2/3”,

medium confidence

)

Now your task is to write tests for the following

additional cases…

1. 1 (k=1)

2. 1 (k=2)

3. 10 (k=1)

4. 10 (k=2)

}

TODO 4/5: Ok – now it’s time to put some pieces together!!

Finish designing the function yesNoClassifier below –

you’ve been provided with guiding steps, as well as

tests that should pass, including those that are

incorrect (with lots of confidence!).

we’ll generally use k=3 in our classifier

val classifierK = 3

fun yesNoClassifier(s: String): ResultWithVotes<Boolean> {

1. Convert the input to lowercase

(since) the data set is all lowercase

2. Check to see if the lower-case input

shows up exactly within the dataset

(you can assume there are no duplicates)

3. If the input was found, simply return its label with 100%

confidence (3/3); otherwise, return the result of

performing a 3-NN classification using the dataset and

Levenshtein distance metric.

}

@EnabledTest

fun testYesNoClassifier() {

testSame(

yesNoClassifier(“YES”),

ResultWithVotes(true, 3),

“YES: 3/3”,

)

testSame(

yesNoClassifier(“no”),

ResultWithVotes(false, 3),

“no: 3/3”,

)

testSame(

yesNoClassifier(“nadda”),

ResultWithVotes(false, 2),

“nadda: 2/3”,

) pretty good ML!

testSame(

yesNoClassifier(“yerp”),

ResultWithVotes(true, 3),

“yerp: 3/3”,

) pretty good ML!

testSame(

yesNoClassifier(“ouch”),

ResultWithVotes(true, 3),

“ouch: 3/3”,

) seems very confident in this wrong answer…

testSame(

yesNoClassifier(“now”),

ResultWithVotes(false, 3),

“now 3/3”,

) seems very confident, given the input doesn’t make sense?

}

TODO 5/5: Now that you have a sense of how this approach works,

including some of the (confident) mistakes it can make,

uncomment the following lines to have a classifier

(that we could use side-by-side with our heuristic).

fun isPositiveML(s: String): Boolean = yesNoClassifier(s).label

@EnabledTest

fun testIsPositiveML() {

correctly responds with positive (rote memorization)

for (i in 0..8) {

helpTestElement(i, true, ::isPositiveML)

}

correctly responds with negative (rote memorization)

for (i in 9..17) {

helpTestElement(i, true, ::isPositiveML)

}

}

—————————————————————–

Final app!

—————————————————————–

Whew! You’ve done a lot 🙂

Now let’s put it together and study!!

TODO 1/2: Design the program studyDeck2 that uses the

reactConsole function to study through a

supplied deck using a supplied classifier to

interpret self-reported correctness.

The program should produce the following data:

represents the result of a study session:

how many questions were originally in the deck,

how many total attempts were required to get

them all correct!

data class StudyDeckResult(val numQuestions: Int, val numAttempts: Int)

Look back to the process you followed for studyDeck in

part 1 of the project: you’ll first want to design a

state type, then build the main reactConsole function,

and finally design all the handlers (and don’t forget

to test ALL functions, including the program!).

In case it helps, here’s a trace of a short example

study session (using the simple classifier), with

notes indicated by “<–“

What is the capital of Massachusetts, USA?

Think of the result? Press enter to continue

<– user just pressed enter, so “”

Boston

Correct? (Y)es/(N)o

yup

What is the capital of California, USA?

Think of the result? Press enter to continue

Sacramento

Correct? (Y)es/(N)o

no 🙁 <– cycles Cali to the back!

What is the capital of the United Kingdom?

Think of the result? Press enter to continue

London

Correct? (Y)es/(N)o

YES!

What is the capital of California, USA?

Think of the result? Press enter to continue

Sacramento

Correct? (Y)es/(N)o

yessir!

Questions: 3, Attempts: 4 <– useful summary of return

Some useful prompts

val studyThink = “Think of the result? Press enter to continue”

val studyCheck = “Correct? (Y)es/(N)o”

TODO 2/2: Finally, design the program study2 that…

a) Uses chooseMenuOption to select from amongst a

list of decks; the options must include at least

one deck read from a file (using

readTaggedFlashCardsFile), one generated by code

(using PerfectSquaresDeck), and one that filters

based upon a tag being present (e.g., only

“hard” cards from a list; this may be the cards

read from a file).

b) If the menu in (a) didn’t result in quitting, then

uses chooseMenuOption again to select from amongst

the two sentiment analysis functions.

c) If the menu in (b) didn’t result in quitting, then

uses studyDeck2 to study through the selected deck

with the selected sentiment analysis function.

d) Returns to (a) and continues until either of the

two menus indicate a desire to quit.

Make sure to provide tests that capture (at least)…

– Quitting at the selection of decks

– Quitting at the selection of sentiment analysis

functions

– Studying through at least one deck

some useful labels

val optSimple = “Simple Self-Report Evaluation”

val optML = “ML Self-Report Evaluation”

—————————————————————–

fun main() {

}

runEnabledTests(this)

main()

Project: Part 2,Solution
$30.00 $24.00