Monday, February 02, 2009

Scala Puzzler and IntelliJ

Today I received a serial number for the highly regarded IntelliJ. The license was courtesy of the San Fran JUG, as a thank you for speaking there last month. I used to use IntelliJ many years ago, when I worked in consulting. Back then its refactoring was much more powerful than Eclipse and NetBeans was a joke. I haven't used it too much since then, as I became quite the Eclipse convert. Every once in awhile I will take a look at it. Today I thought I should give it more of a try, since I had this license for it. So during my lunch, I set it up, got its Scala plugin, and solved a Facebook puzzle. Actually I shouldn't say that I solved the puzzle, my solution is a naive, inefficient solution. Anyways, here's the code

import java.io._
import scala.collection.mutable._
import scala.io._

object PeakTraffic {
def main(args:Array[String]){
val users = HashSet.empty[User]
val stream = if (args.length == 1) new FileInputStream(args(0))
else Thread.currentThread.getContextClassLoader.getResourceAsStream("traffic.txt")
val src = Source.fromInputStream(stream)
src.getLines.foreach((str) => {
val emails = str.split(" ").filter(_.contains("@")).map(_.trim)
val sendingUser = User(emails(0))
val receivingUser = User(emails(1))
users(sendingUser) match {
case true => sendingUser = users.find(_ == sendingUser).get
case false => users += sendingUser
}
users(receivingUser) match {
case true => receivingUser = users.find(_ == receivingUser).get
case false => users += receivingUser
}
sendingUser <==> receivingUser
})
makeClusters(users).foreach(println(_))
}

def makeClusters(users:Collection[User])={
val clusters = new ArrayBuffer[List[User]]
users.foreach(_.friends.foreach((friend) =>{
clusters += friend :: friend.friends.filter(friend.isFriend(_))
}))
clusters.filter(_.size >= 3).map(_.sort(_ < _)).toList.removeDuplicates.map(_.mkString(", ")).sort(_ < _)
}
}
case class User(email:String) extends Ordered[User]{
private val friendSet = HashSet.empty[User]
private val sentSet = HashSet.empty[User]
private val receivedSet = HashSet.empty[User]

def compare(that: User) = email compareTo that.email
override def toString = email
def friends = friendSet.toList
def isFriend(friend:User) = friendSet(friend)
def <==> (user:User) = {
this >> user
user << this
}
private
def >>(user:User) = {
sentSet(user) match {
case false => {
receivedSet(user) match {
case true => {
friendSet += user
receivedSet -= user
}
case false => sentSet += user
}
}
}
}
def <<(user:User) = {
receivedSet(user) match {
case false => {
sentSet(user) match {
case true => {
friendSet += user
sentSet -= user
}
case false => receivedSet += user
}
}
}
}
}

A couple of things about IntelliJ... Code completion seemed much slower than on NetBeans or Eclipse. Similarly most errors weren't flagged until the code was compiled (maybe it's this way with Java, too?) Debugging was good. Some refactoring is supported. I think IntelliJ is unique in this regard. However, I couldn't rename a method to an operator symbol (>> in this case.) Also, method extraction did not seem to work, which is probably the refactoring technique I use the most when I code. The indentation was also flaky. Like I could create a class, hit return (indented) create a val, hit return (indented even more), etc. I think the lack of semicolons might have been causing it problems, but that's just a guess. Overall, I think the NetBeans plugin is better, but at least my experience wasn't as bad as David's.

No comments: