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

Toni Day 4 #11

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 73 additions & 0 deletions tonilopezmr/day4/PassphraseValidator.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package day4

import org.junit.Assert.*
import org.junit.Test
import java.io.File
import java.util.*

class PassphraseValidator {

fun validI(passphrase: String): Boolean {
val words = passphrase
.split(" ")
return words
.foldIndexed(true) { idx, acc, a ->
if (words.subList(idx + 1, words.size).contains(a)) {
false
} else {
acc
}
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess you could simplify this to

return words.all { word -> words.count { it == word } <= 1 }  

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wooooot?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

all checks all the elements in a collection fulfill a predicate.

count counts how many items in a collection fulfill a predicate.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

too much magic behind this methods

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Declarative magic 😉


fun valid(passphrase: String): Boolean {
val list = LinkedList(passphrase.split(" "))
val element = list.poll()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose you are using LinkedList to get Dequeue behaviour. But did you know you could the same with an immutable list? (Just a bit uglier though, but in Kategory they have implemented methods for (x : xs) haskell notation like)

val list = listOf(1,2,3)
val (head, tail) = Pair(list.first, list.drop(1))
// head -> 1
// tail -> listOf(2,3)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are droping in the same way, Yes I did with LinkedList to work like (x :xs)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Except, drop is immutable. It returns a new list.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, In kategory, we have several issues with OutOfMemory with drop in ListKW.fold for many elements 👎

return validR(list, element)
}

tailrec fun validR(passphrase: LinkedList<String>, element: String): Boolean =
if (passphrase.isEmpty()) true
else if (passphrase.contains(element)) false
else {
val element = passphrase.poll()
validR(passphrase, element)
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are you using tailrec? Did the IDE suggested it to you or you knew you had to use it?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No! tailrec is for recursion optimization, tailrec coerce to call the method only in the last operation in return https://kotlinlang.org/docs/reference/functions.html and https://medium.com/@JorgeCastilloPr/tail-recursion-and-how-to-use-it-in-kotlin-97353993e17f

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting, I will search if Scala has a tailrec reserved word too 👍


fun countValid(passphrase: List<String>): Int = passphrase
.fold(0) { acc, a -> if (valid(a)) acc + 1 else acc }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could simplify this like this

fun countValid(passphrase: List<String>): Int = passphrase.count(::valid)


@Test
fun `valid if no words duplicated`() {
assertTrue(valid("aa bb cc dd"))
}

@Test
fun `invalid if words duplicated`() {
assertFalse(valid("aa bb cc dd aa"))
}

@Test
fun `is valid - aa and aaa count as different words`() {
assertTrue(valid("aa bb cc dd aaa"))
}

@Test
fun `in list with 5 passphrase elements only 2 are valid`() {
assertEquals(2, countValid(listOf(
"aa bb cc dd cc",
"miguel say my name miguel",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😏

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💘

"password",
"aa bb cc dd hh jja",
"aa bb cc dd hh jj aa"
)))
}

@Test
fun `result for the challenge of day 4 is`() {
val sc = Scanner(File("input"))
val input = mutableListOf<String>()
while (sc.hasNext()) input.add(sc.nextLine())
assertEquals(386, countValid(input))
}
}
108 changes: 108 additions & 0 deletions tonilopezmr/day4/PassphraseValidatorPartTwo.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package day4


import org.junit.Assert.*
import org.junit.Test
import java.io.File
import java.util.*

class PassphraseValidatorPartTwo {

fun validI(passphrase: String): Boolean {
val words = passphrase
.split(" ")
return words
.foldIndexed(true) { idx, acc, a ->
acc && words.subList(idx + 1, words.size)
.fold(true) { bcc, b ->
bcc && !equalsInAnyOrder(a, b)
}
}
}

fun containsInAnyOrder(passphrase: List<String>, element: String): Boolean =
passphrase.fold(false) { bcc, b ->
if (equalsInAnyOrder(element, b)) true
else bcc
}

fun equalsInAnyOrder(a: String, b: String): Boolean = a.fold(true) { acc, it ->
if (a.length != b.length) false else acc && b.contains(it)
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is okay but you can check if you strings are anagrams if you sort them and they are equal. That will simplify your solution a lot 👍

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes but I don't what is better for performance? good thought

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know what will be the cost of sort integrated method. But right now you have 4 nested loops. @JoseLlorensRipolles and me only have 2, I think, and we are using this approach.


fun valid(passphrase: String): Boolean {
val list = LinkedList(passphrase.split(" "))
val element = list.poll()
return validR(list, element)
}

tailrec fun validR(passphrase: LinkedList<String>, element: String): Boolean =
if (passphrase.isEmpty()) true
else if (containsInAnyOrder(passphrase, element)) false
else {
val element = passphrase.poll()
validR(passphrase, element)
}

fun countValid(passphrase: List<String>): Int = passphrase
.fold(0) { acc, a -> if (valid(a)) acc + 1 else acc }

@Test
fun `ab contains in any order ba`() {
assertTrue(equalsInAnyOrder("ab", "ba"))
}

@Test
fun `aa contains in any order aa`() {
assertTrue(equalsInAnyOrder("aa", "aa"))
}

@Test
fun `ab not contains in any order bc`() {
assertFalse(equalsInAnyOrder("ab", "bc"))
}

@Test
fun `aa not contains in any order aaa`() {
assertFalse(equalsInAnyOrder("aa", "aaa"))
}

@Test
fun `aa bb cc dd contains in any order`() {
assertTrue(containsInAnyOrder("aa bb cc dd".split(" "), "aa"))
}

@Test
fun `valid if no words duplicated`() {
assertTrue(valid("aa bb cc dd"))
}

@Test
fun `invalid if words duplicated`() {
assertFalse(valid("aa bb cc dd aa"))
}

@Test
fun `is valid - aa and aaa count as different words`() {
assertTrue(valid("aa bb cc dd aaa"))
}

@Test
fun `in list with 5 passphrase elements only 2 are valid`() {
assertEquals(2, countValid(listOf(
"ab bc cd de cb",
"miguel say my name lguiem",
"password",
"aa bb cc dd hh jja",
"aa bb cc dd hh jj aa"
)))
}

@Test
fun `result for the challenge of day 4 is`() {
val sc = Scanner(File("input"))
val input = mutableListOf<String>()
while (sc.hasNext()) input.add(sc.nextLine())
assertEquals(208, countValid(input))
}
}