changing [list] - list

Okay so I need to update the data in a list by adding a new figure at the front of the list and remove the oldest figure:
type Place = (String, Float, Float, [Int])
testData :: [Place]
testData = [("London", 51.5, -0.1, [0,0,5,8,8,0,0]),
("Cardiff", 51.5, -3.2, [12,8,15,0,0,0,2]),
("Norwich", 52.6, 1.3, [0,6,5,0,0,0,3]),
("Birmingham", 52.5, -1.9, [0,2,10,7,8,2,2]),
("Liverpool", 53.4, -3.0, [8,16,20,3,4,9,2]),
("Hull", 53.8, -0.3, [0,6,5,0,0,0,4]),
("Newcastle", 55.0, -1.6, [0,0,8,3,6,7,5]),
("Belfast", 54.6, -5.9, [10,18,14,0,6,5,2]),
("Glasgow", 55.9, -4.3, [7,5,3,0,6,5,0]),
("Plymouth", 50.4, -4.1, [4,9,0,0,0,6,5]),
("Aberdeen", 57.1, -2.1, [0,0,6,5,8,2,0]),
("Stornoway", 58.2, -6.4, [15,6,15,0,0,4,2]),
("Lerwick", 60.2, -1.1, [8,10,5,5,0,0,3]),
("St Helier", 49.2, -2.1, [0,0,0,0,6,10,0])]
I tried the below function
updateRain :: Int -> Place -> Place
updateRain x info = [info|(city,long,lat,rain)<- info, rain == newRain]
where newRain = x : init rain
demo = zipWith updateRain [0,8,0,0,5,0,0,3,4,2,0,8,0,0] testData
I'm getting this error when I try to load this:
Couldn't match type ‘[Place]’
with ‘(String, Float, Float, [Int])’
Expected type: Place
Actual type: [Place]
• In the expression:
[info | (city, long, lat, rain) <- info, rain == newRain]
In an equation for ‘updateRain’:
updateRain x info
= [info | (city, long, lat, rain) <- info, rain == newRain]
where
newRain = x : init rain
|
43 | updateRain x info = [info|(city,long,lat,rain)<- info, rain == newRain]
Anyone know where I'm going wrong? I want the parameters taken in to be the new data and the testData and the output to be the testData with the rain being updated to the new figure at the head and removing the last figure in each list.

Your updateRain function uses list comprehension, that means that info in … <- info should be a list here, but it is just a Place object based on your signature.
You can use pattern matching instead:
updateRain :: Int -> Place -> Place
updateRain x (city,long,lat,rain) = (city, long, lat, x : init rain)
This can fail if rain is an empty list, since init errors on an empty list. You might want to make a separate case for that:
updateRain :: Int -> Place -> Place
updateRain x (city,long,lat,[]) = …
updateRain x (city,long,lat,rain#(_:_)) = (city, long, lat, x : init rain)

Related

Calculating the value of a field based on the difference between the values of another field in two adjacent positions using haskell

I have a list of custom data objects which track an increasing total value on a daily basis using one field total. Another field in the custom data type is the value new. Using a csv file I have read in the values for date and total and am trying to calculate and set the values for new from these values.
data item = Item{
date :: Day,
total :: Int,
new :: Int
}
Before
date
total
new
01/01/2021
0
0
02/01/2021
2
0
03/01/2021
6
0
04/01/2021
15
0
After
date
total
new
01/01/2021
0
0
02/01/2021
2
2
03/01/2021
6
4
04/01/2021
15
9
My understanding is that in haskell I should be trying to avoid the use of for loops which iterate over a list until the final row is reached, for example using a loop control which terminates upon reaching a value equal to the length of the list.
Instead I have tried to create a function which assigns the value of new which can used with map to update each item in the list. My problem is that such a function requires access to both the item being updated, as well as the previous item's value for total and I'm unsure how to implement this in haskell.
--Set daily values by mapping single update function across list
calcNew:: [Item] -> Int -> [Item]
calcNew items = map updateOneItem items
-- takes an item and a value to fill the new field
updateOneItem :: Item -> Int -> Item
updateOneItem item x = Item date item total item x
Is it possible to populate that value while using map? If not, is a recursive solution required?
We can do this by zipping the input list with itself, shifted by one step.
Assuming you have a list of items already populated with total values, which you want to update to contain the correct new values (building an updated copy of course),
type Day = Int
data Item = Item{ -- data Item, NB
date :: Day,
total :: Int,
new :: Int
} deriving Show
calcNews :: [Item] -> [Item]
calcNews [] = []
calcNews totalsOK#(t:ts) = t : zipWith f ts totalsOK
where
f this prev = this{ new = total this - total prev }
This gives us
> calcNews [Item 1 0 0, Item 2 2 0, Item 3 5 0, Item 4 10 0]
[Item {date = 1, total = 0, new = 0},Item {date = 2, total = 2, new = 2},
Item {date = 3, total = 5,new = 3},Item {date = 4, total = 10, new = 5}]
Of course zipWith f x y == map (\(a,b) -> f a b) $ zip x y, as we saw in your previous question, so zipWith is like a binary map.
Sometimes (though not here) we might need access to the previously calculated value as well, to calculate the next value. To arrange for that we can create the result by zipping the input with the shifted version of the result itself:
calcNews2 :: [Item] -> [Item]
calcNews2 [] = []
calcNews2 (t:totalsOK) = newsOK
where
newsOK = t : zipWith f totalsOK newsOK
f tot nw = tot{ new = total tot - total nw }

How to create a Hashtbl with a custom type as key?

I'm trying to create a Hashtbl with a node type I've written.
type position = float * float
type node = position * float
I'd like to create a Hashtbl with nodes as keys pointing to a float, and have something like this :
[((node), float))]
This is what I've tried so far :
module HashtblNodes =
struct
type t = node
let equal = ( = )
let hash = Hashtbl.hash
end;;
Along with :
module HashNodes = Hashtbl.Make(HashtblNodes);;
I'm not sure it's the right implementation to do what I explained before, plus I don't know how I could create the table with this.
How would I be able to do this please?
Your approach just works (though see a comment to your question about "you don't actually need to use the functor").
Starting from your definitions in the question:
# let tbl = HashNodes.create 1 ;;
val tbl : '_weak2 HashNodes.t = <abstr>
# let node1 = ((1.0, 2.0), 3.0);;
val node1 : (float * float) * float = ((1., 2.), 3.)
# let node2 = ((-1.0, -2.0), -3.0);;
val node2 : (float * float) * float = ((-1., -2.), -3.)
# HashNodes.add tbl node1 100.0;;
- : unit = ()
# HashNodes.add tbl node2 200.0;;
- : unit = ()
# HashNodes.find tbl ((1.0, 2.0), 3.0) ;;
- : float = 100.
#

Printing Lists in Haskell new

Brand new to haskell and I need to print out the data contained on a seperate row for each individual item
Unsure on how to
type ItemDescr = String
type ItemYear = Int
type ItemPrice = Int
type ItemSold = Int
type ItemSales = Int
type Item = (ItemRegion,ItemDescr,ItemYear,ItemPrice,ItemSold,ItemSales)
type ListItems = [Item]
rownumber x
| x == 1 = ("Scotland","Desktop",2017,900,25,22500)
| x == 2 = ("England","Laptop",2017,1100,75,82500)
| x == 3 = ("Wales","Printer",2017,120,15,1800)
| x == 4 = ("England","Printer",2017,120,60,7200)
| x == 5 = ("England","Desktop",2017,900,50,45000)
| x == 6 = ("Wales","Desktop",2017,900,20,18000)
| x == 7 = ("Scotland","Printer",2017,25,25,3000)
showall
--print??
So for example on each individual line
show
"Scotland","Desktop",2017,900,25,22500
followed by the next record
Tip 1:
Store the data like this
items = [("Scotland","Desktop",2017,900,25,22500),
("England","Laptop",2017,1100,75,82500),
("Wales","Printer",2017,120,15,1800),
("England","Printer",2017,120,60,7200),
("England","Desktop",2017,900,50,45000),
("Wales","Desktop",2017,900,20,18000),
("Scotland","Printer",2017,25,25,3000)]
Tip 2:
Implement this function
toString :: Item -> String
toString = undefined -- do this yourselves
Tip 3:
Try to combine the following functions
unlines, already in the Prelude
toString, you just wrote it
map, does not need any explanation
putStrLn, not even sure if this is a real function, but you need it anyway.
($), you can do without this one, but it will give you bonus points

match a timestamp based on regex pattern matching scala

I wrote the following code :
val reg = "([\\d]{4})-([\\d]{2})-([\\d]{2})(T)([\\d]{2}):([\\d]{2})".r
val dataExtraction: String => Map[String, String] = {
string: String => {
string match {
case reg(year, month, day, symbol, hour, minutes) =>
Map(YEAR -> year, MONTH -> month, DAY -> day, HOUR -> hour)
case _ => Map(YEAR -> "", MONTH -> "", DAY -> "", HOUR -> "")
}
}
}
val YEAR = "YEAR"
val MONTH = "MONTH"
val DAY = "DAY"
val HOUR = "HOUR"
This function is supposed to be applied to strings having the following format: 2018-08-22T19:10:53.094Z
When I call the function :
dataExtractions("2018-08-22T19:10:53.094Z")
Your pattern, for all its deficiencies, does work. You just have to unanchor it.
val reg = "([\\d]{4})-([\\d]{2})-([\\d]{2})(T)([\\d]{2}):([\\d]{2})".r.unanchored
. . .
dataExtraction("2018-08-22T19:10:53.094Z")
//res0: Map[String,String] = Map(YEAR -> 2018, MONTH -> 08, DAY -> 22, HOUR -> 19)
But the comment from #CAustin is correct, you could just let the Java LocalDateTime API handle all the heavy lifting.
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter._
val dt = LocalDateTime.parse("2018-08-22T19:10:53.094Z", ISO_DATE_TIME)
Now you have access to all the data without actually saving it to a Map.
dt.getYear //res0: Int = 2018
dt.getMonthValue //res1: Int = 8
dt.getDayOfMonth //res2: Int = 22
dt.getHour //res3: Int = 19
dt.getMinute //res4: Int = 10
dt.getSecond //res5: Int = 53
Your pattern matches only strings that look exactly like yyyy-mm-ddThh:mm, while the one you are testing against has milliseconds and a Z at the end.
You can append .* at the end of your pattern to cover strings that have additional characters at the end.
In addition, let me show you a more idiomatic way of writing your code:
// Create a type for the data instead of using a map.
case class Timestamp(year: Int, month: Int, day: Int, hour: Int, minutes: Int)
// Use triple quotes to avoid extra escaping.
// Don't capture parts that you will not use.
// Add .* at the end to account for milliseconds and timezone.
val reg = """(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}).*""".r
// Instead of empty strings, use Option to represent a value that can be missing.
// Convert to Int after parsing.
def dataExtraction(str: String): Option[Timestamp] = str match {
case reg(y, m, d, h, min) => Some(Timestamp(y.toInt, m.toInt, d.toInt, h.toInt, min.toInt))
case _ => None
}
// It works!
dataExtraction("2018-08-22T19:10:53.094Z") // => Some(Timestamp(2018,8,22,19,10))

Retrieve matching values from branch to leaf

So im trying to make a tree with companies as the branch and subcompanies of the company as leafs.
Every company/subcompany consists of a name, a gross income and a list with their subcompanies.
I've made a function that returns a tuple with (name,gross income) for all the companies/subcompanies in the company tree.
What I would like to do now, is to find the total income for a certain company/subcompany, which means that the function should add all the subcompanies gross income together with the companies and return the result. It should only have a string as input (the name of the company).
type Department = a' list
and a' = | SubDep of string * float
| Company of string * float * Department;;
let company =
Company("Arla", 1000.0, [SubDep("Karolines Køkken", 500.0);
Company("Lurpak", 200.0, [SubDep("Butter",100.0); SubDep("Milk", 100.0)]);
SubDep("Arla Buko", 100.0);
Company("Riberhus", 200.0, [SubDep("Cheese",200.0)])
]);;
So an example run should be:
companyTotalIncome "Arla" company;;
And the result should be: 2400
To sum everything up you can do something like this (don't really know why you'll need the name of the company in here as for your example you just summed it all up):
let rec depIncome =
function
| SubDep (_, income) -> income
| Company (_,income, sub) -> income + totalIncome sub
and totalIncome =
List.sumBy depIncome
but your data structures are real strange (why list of companies a department?)
I would do maybe like this:
type Department =
{ name : String
income : float
}
type Company =
{ name : String
departmens : Department list
subCompanies : Company list
}
and then
let rec income company =
company.departments |> List.sumBy (fun d -> d.income }
+ company.subCompanies |> List.sumBy income
of course you can move the income to the company too (or add one there too) - I really don't know what you want to do - just my 5cts.