Skip to content

Instantly share code, notes, and snippets.

@sortega
Created September 3, 2015 17:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sortega/122e7a332c10aa632a1b to your computer and use it in GitHub Desktop.
Save sortega/122e7a332c10aa632a1b to your computer and use it in GitHub Desktop.
Line count kata
package lines
import java.io.File
import scala.annotation.tailrec
object LineCount {
def of(file: File): Int = {
val source = scala.io.Source.fromFile(file)
try of(source.toStream)
finally source.close()
}
def of(string: String): Int = of(string.toStream)
def of(input: Stream[Char]): Int = countLines(input, nonEmptyLines = 0)
@tailrec
private def countLines(
input: Stream[Char],
nonEmptyLines: Int,
currentLineIsEmpty: Boolean = true): Int = {
def newNonEmptyLines: Int = nonEmptyLines + (if (currentLineIsEmpty) 0 else 1)
input match {
case Stream.Empty => newNonEmptyLines
case '\n' #:: remaining => countLines(remaining, newNonEmptyLines)
case whitespace #:: remaining if Character.isWhitespace(whitespace) =>
countLines(remaining, nonEmptyLines, currentLineIsEmpty)
case '"' #:: remaining =>
countLines(remaining.dropWhile(_ != '"').tail, nonEmptyLines, currentLineIsEmpty = false)
case '/' #:: '/' #:: remaining =>
countLines(remaining.dropWhile(_ != '\n'), nonEmptyLines, currentLineIsEmpty)
case '/' #:: '*' #:: remaining =>
val commentSize = remaining.sliding(2).takeWhile(_ != "*/".toStream).size + 2
val (comment, afterComment) = remaining.splitAt(commentSize)
countLines(
input = afterComment,
nonEmptyLines = if (comment.contains('\n')) newNonEmptyLines else nonEmptyLines
)
case nonWhitespace #:: remaining =>
countLines(remaining, nonEmptyLines, currentLineIsEmpty = false)
}
}
}
package lines
import org.scalatest.{FlatSpec, ShouldMatchers}
class LineCountTest extends FlatSpec with ShouldMatchers {
"Line count" should "have 0 lines for empty files" in {
LineCount.of("") shouldBe 0
}
it should "have 0 lines for lines with just whitespace" in {
LineCount.of(" \n\t\n\n") shouldBe 0
}
it should "count lines with text" in {
LineCount.of(
"""def fun(arg1, arg2): foo
|
|def bar: 3
""".stripMargin) shouldBe 2
}
it should "discard line comments" in {
LineCount.of(
"""// This file contains 3 lines of code
|public interface Dave {
| int countLines(File inFile); // not the real signature!
|}
|
""".stripMargin) shouldBe 3
}
it should "discard block comments" in {
LineCount.of(
"""// This file contains 3 lines of code
|public interface Dave {
| /**
| * count the number of lines in a file
| */
| int countLines(File inFile); // not the real signature!
|}
|
""".stripMargin) shouldBe 3
}
it should "discard bizarre comments" in {
LineCount.of(
"""/*****
| * This is a test program with 5 lines of code
| * \/* no nesting allowed!
| //*****//***/// Slightly pathological comment ending...
|
|public class Hello {
| public static final void main(String [] args) { // gotta love Java
| // Say hello
| System./*wait*/out./*for*/println/*it*/("Hello/*");
| }
|
|}
|
""".stripMargin) shouldBe 5
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment