I just started writing Scala about a month ago. I however have been writing in Java, Javascript and some others.
Please I'd need someone to tell me why this code wouldn't display the result of the move(x,y) method. Even though it builds and runs succesfully.
class PinPoint(val xc: Int, val yc: Int){
var x:Int = xc; var y:Int = yc
def move(dx:Int, dy:Int){
x = x + dx
y = y + dy
println("Position on horizontal axis is " + x);
print("Position on vertical axis is " + y);
}
}
object Run {
def main(args: Array[String]) {
val pos = new PinPoint(20,18);
println()
pos.move(5,7);
}
}
Consider also this approach where a new PinPoint instance is created each time move() is called, namely using (immutable) values instead of (mutable) variables,
case class PinPoint(x: Int, y: Int) {
def move(dx: Int, dy: Int) = PinPoint(x+dx, y+dy)
override def toString() =
s"""|Position on horizontal axis is $x
|Position on vertical axis is $y""".stripMargin
}
object Run {
def main(args: Array[String]) {
val pos = PinPoint(20,18);
println(pos)
val shiftedPos = pos.move(5,7);
println(shiftedPos)
}
}
Method toString() delivers a multiline string where class members x and y have been interpolated.
One way to compile and run it,
scalac PinPoint.scala -d PinPoint.jar
scala PinPoint.jar
and so
Position on horizontal axis is 20
Position on vertical axis is 18
Position on horizontal axis is 25
Position on vertical axis is 25
Related
how to test for the length of a var arg parameter for a contructor.
I am defining a case class polygon which takes in a sequence of points, I want to make sure that the no of points is atleast 5 usinga require clause.
case class Point(x: Int, y: Int)
case class Polygon(points: Point*) {
// require(point >= 3) }
How about this?:
case class Point(x: Int, y: Int)
case class Polygon(a: Point, b: Point, c: Point, d: Point, e: Point, other: Point*) {
def points = Vector(a,b,c,d,e) ++ other
}
Then:
val p1 = Point(1,1)
val p2 = Point(2,1)
val p3 = Point(3,1)
val p4 = Point(4,1)
val p5 = Point(5,1)
val p6 = Point(6,1)
val p7 = Point(7,1)
val polygon5 = Polygon(p1,p2,p3,p4,p5)
println(polygon5.points)
// Vector(Point(1,1), Point(2,1), Point(3,1), Point(4,1), Point(5,1))
val polygon7 = Polygon(p1,p2,p3,p4,p5,p6,p7)
println(polygon7.points)
// Vector(Point(1,1), Point(2,1), Point(3,1), Point(4,1), Point(5,1), Point(6,1), Point(7,1))
Polygon(p1,p2,p3,p4) // error: not enough arguments for method apply
Moving this requirement to compile time by making the class take in 5 Point arguments and then a Point* variadic argument is usually going to be your best bet (as shown by dhg's answer).
If you want to use require instead, it is quite simple:
case class Polygon(points: Point*) {
require(points.length >= 5, "Must have at least 5 points")
}
I've got a box plot (CategoryPlot) with a customBoxAndWhiskerRenderer`. A series of bins forms the categories. I want to mark the transition from one category to another with a vertical line.
For example in the following figure, a line marks the sixth category, however I want it to appear between the fifth and sixth category:
val mark = new CategoryMarker(splitBin)
mark.setDrawAsLine(true)
plot.addDomainMarker(mark, Layer.BACKGROUND)
What is the best/easiest way to achieve this?
Since I already had a custom renderer, I hooked into the drawDomainMarker method, checking for the presence of a custom sub type of CategoryMarker:
class LeftCategoryMarker(key: Comparable[_], paint: Paint, stroke: Stroke)
extends CategoryMarker(key, paint, stroke) {
def this(key: Comparable[_]) = this(key, Color.gray, new BasicStroke(1.0f))
}
Then the draw method diverts:
override def drawDomainMarker(g2: Graphics2D, plot: CategoryPlot, axis: CategoryAxis,
marker: CategoryMarker, dataArea: Rectangle2D): Unit =
marker match {
case _: LeftCategoryMarker => drawLeftMarker(g2, plot, axis, marker, dataArea)
case _ => super.drawDomainMarker(g2, plot, axis, marker, dataArea)
}
private def drawLeftMarker(g2: Graphics2D, plot: CategoryPlot, axis: CategoryAxis,
marker: CategoryMarker, dataArea: Rectangle2D): Unit = {
val cat = marker.getKey
val dataset = plot.getDataset(plot.getIndexOf(this))
val catIdx = dataset.getColumnIndex(cat)
if (catIdx < 0) return
val savedComposite = g2.getComposite
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, marker.getAlpha))
axis.getCategoryMargin
val numCat = dataset.getColumnCount
val domainEdge = plot.getDomainAxisEdge
val left = axis.getCategoryStart (catIdx, numCat, dataArea, domainEdge)
val gap = calculateCategoryGapSize(axis , numCat, dataArea, domainEdge)
val v = left - gap/2
val line = if (plot.getOrientation == PlotOrientation.HORIZONTAL)
new Line2D.Double(dataArea.getMinX, v, dataArea.getMaxX, v)
else
new Line2D.Double(v, dataArea.getMinY, v, dataArea.getMaxY)
g2.setPaint (marker.getPaint )
g2.setStroke(marker.getStroke)
g2.draw(line)
g2.setComposite(savedComposite)
}
And the following protected method from CategoryAxis is needed to calculate the gap:
private def calculateCategoryGapSize(axis: CategoryAxis, categoryCount: Int,
area: Rectangle2D, e: RectangleEdge): Double = {
if (categoryCount == 0) return 0.0
val available = if ((e == RectangleEdge.TOP) || (e == RectangleEdge.BOTTOM))
area.getWidth
else if ((e == RectangleEdge.LEFT) || (e == RectangleEdge.RIGHT))
area.getHeight
else
0.0
available * axis.getCategoryMargin / (categoryCount - 1)
}
I wrote an object PathGraph which implements a graph of Nodes and various useful functions, which I intend to use for pathfinding in a simple tower defense game. I also wrote a class Path which implements Dijkstra's algorithm, and each non-static in-game unit has a Path.
The problem I am running into is that when I run the application, the code executes the code to initialize the units and, in doing so, initialize a path for each creep before building the PathGraph object (confirmed using Eclipse Scala debugger and println statements). Unfortunately however, the code to generate a path requires that the PathGraph object, and specifically the path variable (var so that I can point to a new path if the map gets updated, etc.), be initialized.
How should I fix this problem with my code? PathGraph code pasted below for reference.
object PathGraph {
private val graph:Array[Array[Node]] = buildAndFillGraph()
//val nodeDist:Double = MainGame.pixelsPerIteration
val nodeDist = .5
val numXNodes = (MainGame.gamePanelWidth.toDouble / nodeDist).toInt
val numYNodes = (MainGame.gamePanelHeight.toDouble / nodeDist).toInt
val defaultInfinity = 99999
//build every Nodes adjacent nodes
val angle = 45
val minHeight = 0
val minWidth = 0
val maxHeight = MainGame.gamePanelSize.height //game panel y value starts at 0 at TOP
val maxWidth = MainGame.gamePanelSize.width
val numPossibleAdjacentNodes = 360 / angle //360 degrees, 45 degree angle between every potentially adjacent Node
val hypotenuseLength = math.sqrt((nodeDist * nodeDist) + (nodeDist * nodeDist))
def buildGraphArray(): Array[Array[Node]] = {
println("numXNodes/nodeDist.toInt: " + (numXNodes.toDouble / nodeDist).toInt + "\n")
//build every Node in the graph
val lgraph =
(for (x <- 0 until (numXNodes / nodeDist).toInt) yield {
(for (y <- 0 until (numYNodes / nodeDist).toInt) yield {
new Node(x.toDouble * nodeDist, y.toDouble * nodeDist)//gives lgraph(x,y) notation
}).toArray //convert IndexedSeqs to Arrays
}).toArray//again
lgraph
}
def buildAndFillGraph():Array[Array[Node]] = {
val lgraph = buildGraphArray()//Ar[Ar[Node]]
println("lgraph built")
lgraph.map(x => x.map(y => y.setAdjacentNodes(lgraph)))
//set the adjacent nodes for all nodes in the array
if (lgraph.size != numXNodes*numYNodes) println("numXNodes*numYNodes: " + numXNodes*numYNodes)
else MainGame.pathGraphBuilt = true
lgraph
}
def getGraph() = graph
def toBuffer(): mutable.Buffer[Node] = graph.flatten.toBuffer
def toArray(): Array[Node] = graph.flatten
}
There are a few things you can do to improve the code:
Do not use static variables. Your PathGraph should be a class, not an object. MainGame. pathGraphBuilt is also a static variable that you can replace with a builder - see the next point.
Use a Builder pattern to differentiate between things that build and the end result. Your PathGraph logic will mostly go into the builder. Something along these lines:
-
case class PathGraphBuilder(nodeDist: Double, numXNodes: Double /* and so on */) {
def apply: PathGraph = buildAndFillGraph
def buildGraphArray = ...
def buildAndFillGraph = ...
}
class PathGraph(underlyingGraph: Array[Array[Node]]) {
def toBuffer(): mutable.Buffer[Node] = underlyingGraph.flatten.toBuffer
def toArray(): Array[Node] = underlyingGraph.flatten
}
I have been experimenting with partialy applied functions and such a thing happened. Suppose we have such code:
class Color(name:String) {
override def toString = name
}
class Point(x: Int, y:Int) {
override def toString:String = "("+x +"," + y + ")"
}
class Linestyle(name: String) {
override def toString = name
}
def drawLine(width:Double, color: Color, style: Linestyle, a:Point, b: Point): Unit = {
println("Draw " + width + " " + color + " " + style + " " + " line from point: " + a + " to point " + b)
}
When I try to create drawSolidLine function which takes only 4 parameters in such a way:
def drawSolidLine (c: Color, a:Point, b: Point):Unit =
drawLine(1.0, _:Color, new Linestyle("SOLID"), _:Point, _:Point)
and try to call it
drawSolidLine(2.5, new Color("Black"),new Point(2,4), new Point(3,1))
I have no compiler errors, but the call returns nothing.
On the other hand, when I create drawSolidLine this way:
val drawSolidLine = drawLine(_:Double, _:Color, new Linestyle("SOLID"),
_:Point, _:Point)
and call it before, I have the desired output:
Draw 1.0 Black SOLID line from point: (2,4) to point (3,1)
What I am missing?
You are doing two very different things. First:
def drawSolidLine (c: Color, a:Point, b: Point):Unit =
drawLine(1.0, _:Color, new Linestyle("SOLID"), _:Point, _:Point)
First, notice that none of the parameters you passed are being used. The expression drawLine(1.0, _:Color, new Linestyle("SOLID"), _:Point, :Point) is a function which does not depend on the parameters passed, and which would be returned, except your return type is Unit. That being the case, the function is discarded.
Second:
val drawSolidLine = drawLine(_:Double, _:Color, new Linestyle("SOLID"),
_:Point, _:Point)
First, you can replace val with def and it will work the same. Val vs def is not the issue.
So, drawSolidLine will, since it's type is not Unit, return that same function. This time, however, (2.5, new Color("Black"),new Point(2,4), new Point(3,1)) is not being passed to drawSolidLine, because it takes no parameters. So they'll be passed to the function that is being returned, causing the desired effect.
I'm trying to build some image algebra code that can work with images (basically a linear pixel buffer + dimensions) that have different types for the pixel. To get this to work, I've defined a parametrized Pixel trait with a few methods that should be able to get used with any Pixel subclass. (For now, I'm only interested in operations that work on the same Pixel type.) Here it is:
trait Pixel[T <: Pixel[T]] {
def div(v: Double): T
def div(v: T): T
}
Now I define a single Pixel type that has storage based on three doubles (basically RGB 0.0-1.0), I've called it TripleDoublePixel:
class TripleDoublePixel(v: Array[Double]) extends Pixel[TripleDoublePixel] {
var data: Array[Double] = v
def this() = this(Array(0.0, 0.0, 0.0))
def div(v: Double): TripleDoublePixel = {
new TripleDoublePixel(data.map(x => x / v))
}
def div(v: TripleDoublePixel): TripleDoublePixel = {
var tmp = new Array[Double](3)
tmp(0) = data(0) / v.data(0)
tmp(1) = data(1) / v.data(1)
tmp(2) = data(2) / v.data(2)
new TripleDoublePixel(tmp)
}
}
Then we define an Image using Pixels:
class Image[T](nsize: Array[Int], ndata: Array[T]) {
val size: Array[Int] = nsize
val data: Array[T] = ndata
def this(isize: Array[Int]) {
this(isize, new Array[T](isize(0) * isize(1)))
}
def this(img: Image[T]) {
this(img.size, new Array[T](img.size(0) * img.size(1)))
for (i <- 0 until img.data.size) {
data(i) = img.data(i)
}
}
}
(I think I should be able to do without the explicit declaration of size and data, and use just what has been named in the default constructor, but I haven't gotten that to work.)
Now I want to write code to use this, that doesn't have to know what type the pixels are. For example:
def idiv[T](a: Image[T], b: Image[T]) {
for (i <- 0 until a.data.size) {
a.data(i) = a.data(i).div(b.data(i))
}
}
Unfortunately, this doesn't compile:
(fragment of lindet-gen.scala):145:
error: value div is not a member of T
a.data(i) = a.data(i).div(b.data(i))
I was told in #scala that this worked for someone else, but that was on 2.8. I've tried to get 2.8-rc1 going, but the RC1 doesn't compile for me. Is there any way to get this to work in 2.7.7?
Your idiv function has to know it'll actually be working with pixels.
def idiv[T <: Pixel[T]](a: Image[T], b: Image[T]) {
for (i <- 0 until a.data.size) {
a.data(i) = a.data(i).div(b.data(i))
}
}
A plain type parameter T would define the function for all possible types T, which of course don't all support a div operation. So you'll have to put a generic constraint limiting the possible types to Pixels.
(Note that you could put this constraint on the Image class as well, assuming that an image out of something different than pixels doesn't make sense)