I have recently started learning functional programming on a SCALA. I used the recursive function to print elements of the list, now I would like to print elements starting from the end ("Ann" ...) but also using recursive function, can anyone please help me to transform the code?
object Scala {
val names: List[String] = List("Adam", "Mick", "Ann");
def main(args: Array[String]) {
println(printNames(names))
def (printNames (name: List[String] ) {
if(names.isEmpty) ""
else names.head + (printNames(names.tail);
}
}
}
You can use the following definition:
def printNames(names: List[String]): String = {
if(names.isEmpty) ""
else printNames(names.tail) + " " + names.head
}
Note that this implementation is not tail recursive and cannot be optimized by scala compiler. I strongly advise using an accumulator for the resulting String.
Luis Miguel Mejía Suárez suggests using StringBuilder to avoid expensive String concatenation:
def printNames(names: List[String]): StringBuilder = {
if(names.isEmpty) new StringBuilder("")
else printNames(names.tail).append(" ").append(names.head)
}
def printNames(names: List[String]): String = names.reverse.mkString(" ")
You can reverse the name list and then use mkString with space as a separator. The above one can be used to achieve the same without recursion, as mkString by default uses StringBuilder.
As you were looking for a tail-recursive solution
import scala.annotation.tailrec
def printNames(names: List[String]): String = {
#tailrec
def printNamesHelper(
names: List[String],
acc: StringBuilder = new StringBuilder("")
): String = {
names match {
case Nil => acc.toString.trim
case head :: tail => printNamesHelper(tail, acc.append(head).append(" "))
}
}
printNamesHelper(names)
}
def printNamesReverse(names: List[String]): String = {
#tailrec
def printNamesReverseHelper(
names: List[String],
accum: List[String] = List.empty
): String = {
names match {
case Nil => printNames(accum)
case head :: tail => printNamesReverseHelper(tail, head :: accum)
}
}
printNamesReverseHelper(names)
}
printNames(List("Adam", "Framk"))
printNamesReverse(List("Adam", "Framk"))
Added scastie snippet below:
<script src="https://scastie.scala-lang.org/shankarshastri/0dxWt7K3S8CkkAffliLICQ/7.js"></script>
I have some raw test data that I need to split into a map of format:
Map[String, List[(Int, String, Float)]]
I have managed to read in the data as a list and will give an example of one line of data below:
Oor Wullie Route (GCU),1:City Chambers:0.75f,2:Sir Chris Hoy Velodrome:3.8f,3:People's Palace:2.7f,4:Riverside Museum:5.4f,5:Botanic Gardens:2.4f,6:GCU:3.4f
The above represents the following: A Route, a stage number:stage name:total distance of stage
So each set of 3 values (i.e 1:City Chambers:5) should be added to the [Int, String, Float] section of the map, with the route name being the key.
This is my code so far for reading the file and adding it to a list:
var mapBuffer: Map[String, List[(Int, String, Float)]] = Map()
val fitnessData = "C:\\Users\\josep\\Desktop\\Coursework\\Coursework\\src\\cw.txt"
val lines = Source.fromFile("C:\\Users\\josep\\Desktop\\Coursework\\Coursework\\src\\cw.txt").getLines.toList
I would like to write a funciton for splitting the data up and adding it to a map, essentially doing this:
var key ="Oor Wullie Route (GCU)"
var newList = List((1,"City Chambers",0.75f),(2,"Sir Chris Hoy Velodrome",3.8f),(3,"People's Palace",2.7f),(4,"Riverside Museum",5.4f),(5,"Botanic Gardens",2.4f),(6,"GCU",3.4f))
mapBuffer = mapBuffer ++ Map(key -> newList)
How can I add the data to a map in my desired format?
My suggestion would be to use foldLeft. Something like:
val resource = Source.fromFile("src/lines.txt")
val lines = resource.getLines.toList
resource.close()
val map = lines.foldLeft(Map[String, List[(Int, String, Float)]]())((map, line) => {
val keyValuesArray = line.split(",").toList
val key = keyValuesArray.head
val listOfValuesAsString = keyValuesArray.tail
val listOfValues = listOfValuesAsString.map {
case s"$integer:$string:$float" => (integer.toInt, string, float.toFloat)
}
map + (key -> listOfValues)
})
Start with empty map, and add key->values for each line.
Also, try match expressions when you parse data in list (listOfValues part is doing that).
This approach is with pattern matching and tail recursion.
I think it works very well.
First I convert the file into a List[Array[String]].
Second I call loop to go through the list in a recursive way and build the map.
Third inside the loop function I call make List to build the list of tuples in a recursive way.
As an example:
input
Oor Wullie Route (GCU),1:City Chambers:0.75f,2:Sir Chris Hoy Velodrome:3.8f,3:People's Palace:2.7f,4:Riverside Museum:5.4f,5:Botanic Gardens:2.4f,6:GCU:3.4f
Oor Wullie Route2 (GCU),1:City Chambers:0.75f,2:Sir Chris Hoy Velodrome:3.8f,3:People's Palace:2.7f,4:Riverside Museum:5.4f,5:Botanic Gardens:2.4f,6:GCU:3.4f
Oor Wullie Route3 (GCU),1:City Chambers:0.75f,2:Sir Chris Hoy Velodrome:3.8f,3:People's Palace:2.7f,4:Riverside Museum:5.4f,5:Botanic Gardens:2.4f,6:GCU:3.4f
code
import scala.io.Source
object ConverToMap {
#annotation.tailrec
def makeList(lst: List[String], acc: List[(Int, String, Float)]):List[(Int, String, Float)] = {
lst match {
case Nil => acc
case (h :: t) => {
val data = h.split(":")
val tuple = (data(0).toInt, data(1), data(2).substring(0,data(2).length - 1 ).toFloat)
makeList(t, tuple :: acc)
}
}
}
#annotation.tailrec
def loop(lst: List[Array[String]], acc: Map[String, List[(Int, String, Float)]]): Map[String, List[(Int, String, Float)]] = {
lst match {
case Nil => acc
case (h :: t) => {
val key = h(0)
val lTuple = makeList(h.toList.tail, List())
if(acc.contains(key)) loop(t, acc)
else loop(t, Map(key -> lTuple) ++ acc)
}
}
}
def main(args: Array[String]): Unit = {
val fitnessData = "/home/cloudera/files/tests/to_map.csv"
val lines = Source.fromFile(fitnessData)
.getLines
.toList
.map(line => line.split(","))
val mp = loop(lines, Map())
println(mp)
}
}
expected result
Map(Oor Wullie Route3 (GCU) -> List((6,GCU,3.4), (5,Botanic Gardens,2.4), (4,Riverside Museum,5.4), (3,People's Palace,2.7), (2,Sir Chris Hoy Velodrome,3.8), (1,City Chambers,0.7)),
Oor Wullie Route2 (GCU) -> List((6,GCU,3.4), (5,Botanic Gardens,2.4), (4,Riverside Museum,5.4), (3,People's Palace,2.7), (2,Sir Chris Hoy Velodrome,3.8), (1,City Chambers,0.7)),
Oor Wullie Route (GCU) -> List((6,GCU,3.4), (5,Botanic Gardens,2.4), (4,Riverside Museum,5.4), (3,People's Palace,2.7), (2,Sir Chris Hoy Velodrome,3.8), (1,City Chambers,0.7)))
I'm compiling a lexer using CM and ML-Lex. When I try to compile using CM.make "sources.cm", it throws errors.
errormsg.sml:7.24-7.39 Error: unbound structure: TextIO in path TextIO.instream
errormsg.sml:21.26-21.38 Error: unbound structure: TextIO in path TextIO.stdIn
errormsg.sml:27.18-27.30 Error: unbound structure: TextIO in path TextIO.stdIn
errormsg.sml:36.12-36.24 Error: unbound structure: Int in path Int.toString
and a couple more just like previous ones. If I try to do use "errormsg.sml", everything works perfectly fine. I tried moving errormsg.sml around in sources.cm.
sources.cm :
Group is
$/smlnj-lib.cm
driver.sml
tokens.sig
tokens.sml
errormsg.sml
tiger.lex
errormsg.sml:
signature ERRORMSG =
sig
val anyErrors : bool ref
val fileName : string ref
val lineNum : int ref
val linePos : int list ref
val sourceStream : TextIO.instream ref
val error : int -> string -> unit
exception Error
val impossible : string -> 'a (* raises Error *)
val reset : unit -> unit
end
structure ErrorMsg : ERRORMSG =
struct
val anyErrors = ref false
val fileName = ref ""
val lineNum = ref 1
val linePos = ref [1]
val sourceStream = ref TextIO.stdIn
fun reset() = (anyErrors:=false;
fileName:="";
lineNum:=1;
linePos:=[1];
sourceStream:=TextIO.stdIn)
exception Error
fun error pos (msg:string) =
let fun look(a::rest,n) =
if a<pos then app print [":",
Int.toString n,
".",
Int.toString (pos-a)]
else look(rest,n-1)
| look _ = print "0.0"
in anyErrors := true;
print (!fileName);
look(!linePos,!lineNum);
print ":";
print msg;
print "\n"
end
fun impossible msg =
(app print ["Error: Compiler bug: ",msg,"\n"];
TextIO.flushOut TextIO.stdOut;
raise Error)
end
You need to add $/basis.cm to your sources.cm. This will import the Standard ML basis library:
Group is
$/basis.cm
$/smlnj-lib.cm
driver.sml
tokens.sig
tokens.sml
errormsg.sml
tiger.lex
I am trying to match mathematical operations using a match in scala. So the function will be able to match any string like "5+2" or "log10" or "10^5" etc. However the match keeps failing for the individual types of expressions
def isValid(expression:String):Boolean={
val number = """((\-|\+)?[0-9]+\.?[0-9])*"""
val operation = """([\+,\-,*,/,C,P])"""
val functions = """(log|ln|sin|cos|tan|arc sin|arc cos|arc tan|sec|csc|cot)"""
val powers = """\^"""+number
val arithmeticExpression = (number + operation + number).r
val functionExpression = (functions + number).r
val powerOperation = (number + powers).r
val stringToTest: Regex = ("""(""" +arithmeticExpression+"""|"""+functionExpression+"""|"""+powerOperation+""")""").r
expression match {
case arithmeticExpression(s) => true
case functionExpression(s) => true
case powerOperation(s)=>true
case _ => false
}
}
println(isValid("1+4").toString)
However if I match for a general expression I get the expected output:
def isValid(expression:String):Boolean={
val number = """(\-|\+)?[0-9]+\.?[0-9]*"""
val operation = """[\+,\-,*,/,C,P]"""
val functions = """(log|ln|sin|cos|tan|arc sin|arc cos|arc tan|sec|csc|cot)"""
val power = """\^"""+number
val arithmeticExpression = number+operation+number
val functionExpression = functions+number
val powerExpression = number+power
val validExpression = """(""" +arithmeticExpression+"""|"""+functionExpression+"""|"""+powerExpression+""")"""
validExpression.r.findFirstIn(expression) match {
case Some(`expression`) => true
case None => false
}
You're not doing numbers correctly:
scala> arithmeticExpression.findFirstIn("1+4")
res2: Option[String] = Some(+)
scala> arithmeticExpression.unapplySeq("1+4")
res3: Option[List[String]] = None
scala> arithmeticExpression.unapplySeq("11+14")
res4: Option[List[String]] = Some(List(11, null, +, 14, null))
Since you're requiring two digits.
The "()" in the regular expression for numbers was affecting the result. Also /^ needed to wrapped in (). This ended up working for me.
def isValid(expression:String):Boolean={
val number = """[\-,\+]?[0-9]+\.?[0-9]*"""
val operation = """([\+,\-,*,/,C,P])"""
val functions = """(log|ln|sin|cos|tan|arc sin|arc cos|arc tan|sec|csc|cot)"""
val powers = """(\^)"""+number
val arithmeticExpression = (""""""+number + operation + number+"""""").r
val functionExpression = (functions + number).r
val powerOperation = (number + powers).r
val stringToTest: Regex = ("""(""" +arithmeticExpression+"""|"""+functionExpression+"""|"""+powerOperation+""")""").r
expression match {
case arithmeticExpression(s) => {
println("Arithmetic Match")
true
}
case functionExpression(s) => {
println("Function Match")
true
}
case powerOperation(s)=>{
println("Power Match")
true
}
case _ => false
}
}
Thanks for the help!
Lets take an example where store is my class.
class store(val a:Int) { }
In the code i want to create a list of store.
val list : List[store] = new List[store]()
How can i add a store in the same list?
First off, it's usually a good idea to capitalize your class names.
scala> class Store(val a:Int) { }
defined class Store
scala> val list : List[Store] = List.empty
list: List[Store] = List()
scala> val newList = new Store(4) :: list
newList: List[Store] = List(Store#243306de)
Your list is, by default, immutable so you'll have a new list every time an element is added.
scala> val newerList = new Store(71) :: newList
newerList: List[Store] = List(Store#30f57af0, Store#243306de)
ADDENDUM
If you need a mutable list (not usually recommended) you could try the following.
scala> import scala.collection.mutable.MutableList
import scala.collection.mutable.MutableList
scala> val myList: MutableList[Store] = MutableList.empty
myList: scala.collection.mutable.MutableList[Store] = MutableList()
scala> myList += new Store(56)
res322: myList.type = MutableList(Store#6421614e)
scala> myList += new Store(29)
res323: myList.type = MutableList(Store#6421614e, Store#85b26)
scala> myList += new Store(11)
res324: myList.type = MutableList(Store#6421614e, Store#85b26, Store#5d2f7883)
Mutable variables are considered poor style and an impediment to proper Functional Programming.
To add element to the beginning of the list use :: :
val l = List(1, 2, 3)
val l1 = 5 :: l // List(5, 1, 2, 3)
or
val l1 = l.::(5)
To add element to the end of the list use :+ :
val l2 = l :+ 5 // List(1, 2, 3, 5)
So, to add store object to the end of the list (though it is not efficient), write this:
val s = new Store(1)
val newList = list :+ s // list is immutable