I have a bit of a long winded question. Say I have a method which makes a math calculation and this method needs to keep state (i.e. every time it gets called with parameters its result is affected by its previous results).
int sum
def add(int x, int y) {
sum = sum + x + y
return sum
}
On top of this, its state is dependent on where it was called. (i.e. if I call this method twice from different places the method must only work with its own state). I was thinking about Closures in groovy but closures doesn't keep state and if you store your state in a variable outside of the closure a second call to this method won't have its own state.
Lets also assume for reasons that I won't go into right now I cannot keep this method in a object to keep state. i.e.
class MyObject {
private int sum
MyObject() {}
public int sum(int x, int y) {
sum = sum + x + y
return sum
}
}
I want to avoid doing this
MyObject mObj = new MyObject()
.... some code later
def result = mObj.add(1,2)
instead I just want to have to do this
for (int i = 0; i<5;i++){
def result1 = add(1,2)
}
some code later
for (int i = 0; i<5;i++){
def result2 = add(1,2)
}
In the case above the two 'add' methods should have its own state.
Is there any other tricks in either groovy or java to achieve something like this?
What you are describing here is a closure. The Groovy code there does
already that. If you want put that "global" state into the function, you
could be explicit about it and "build" the add function. E.g.
def add = { ->
def total = 0
return { a, b ->
return total += a + b
}
}()
println(add(1,2))
// -> 3
println(add(1,2))
// -> 6
println(add(1,2))
// -> 9
Above code creates a function, that holds the state for total and
returns a closure, that does the actual add. It captures the local
total. Then this function is called and the result (the closure) is
assigned to add.
edit
As stated in the comments, the expectation is a "magic" boundary that
isolates the state of add over the course of the execution of the
program. Assuming that this question aims for a DSL, users might be
able to know the implicit boundaries. Yet I doubt there is a way with
Groovy (or Java?) to know, that a function is called from inside
a for-loop unless you write your own for.
One way around this would be to create an explicit boundary like this:
def withTotal(c) {
def total = 0
c.add = { total += it }
c.call()
total
}
println withTotal{
add(1)
add(2)
}
// -> 3
println withTotal{
10.times{ add it }
}
// -> 45
Related
public static void calculate(List<Person> data, String categoryType) {
for(int i = 0; i < categoryData.size(); i++) {
if(data.get(i).calculateCategoryOne() == firstPlace) {
...
}
}
}
If you see data.get(i).calculateCategoryOne(), the method call is for category one. The problem is that I need to copy-paste the entire code in a if-block for each category to just change this method call data.get(i).calculateCategoryTwo(), data.get(i).calculateCategoryThree(), ... data.get(i).calculateCategoryTen(),
While I can still make the logic work in this way, I feel it is redundant and not a good programming practice. Just to change one line of code, I would have to replicate the same code ten different times which will add nearly 500 lines of code.
So, my question is: Is there a way to dynamically change my method call based on the category type string argument.
I was thinking one possible way is to pass the method call in a string and convert it to a method call itself. For example, let's assume CategoryType string argument is "calculateCategoryOne()". So, data.get(i)."calculateCategoryOne()" would be recognized by the compiler as the method call itself. Is there a way to actually implement this?
I'm open to other ideas as well to reduce redundancy.
I would think using a functional interface would be appropriate here. You want different functionality depending on the categoryType, so passing in the function you want to use, rather than a String representation of it, would accomplish this.
#FunctionalInterface
public interface Calculate {
int calculate(Person data);
}
public static void calculate(List<Person> data, Calculate calculate) {
for(int i = 0; i < categoryData.size(); i++) {
if(calculate.calculate(data.get(i)) == firstPlace) {
...
}
}
}
and the call to the method would define what the calculation would be
calculate(list, p -> {
// calculation done here
});
or if this would happen frequently, you could predefine your categories once and pass those in:
Calculate categoryOne = p -> { ... };
Calculate categoryTwo = p -> { ... };
.
.
calculate(list, categoryOne);
Below function creates a Map, gets the count of passengers where passengers are > minTrips. The code works completely fine. Please see below
fun List<Trip>.filter(minTrips : Int): Set<Passenger> {
var passengerMap: HashMap<Passenger, Int> = HashMap()
this.forEach { it: Trip ->
it.passengers.forEach { it: Passenger ->
var count: Int? = passengerMap.get(it)
if (count == null) {
count = 1
passengerMap.put(it, count)
} else {
count += 1
passengerMap.put(it, count)
}
}
}
val filteredMinTrips: Map<Passenger, Int> = passengerMap.filterValues { it >= minTrips }
println (" Filter Results = ${filteredMinTrips}")
return filteredMinTrips.keys
}
Even though this is written in Kotlin, it seems like the code was first written in Java and then converted over to Kotlin. If it was truly written in Kotlin I am sure this wouldnt have been so many lines of code. How can I reduce the lines of Code? What would be a more funtional approach to solve this? What function or functions can I use to extract the Passengers Set directly where Passengers are > minTrips? This is too much of a code and seems crazy. Any pointers would be helpful here.
One way you could do this is to take advantage of Kotlin's flatmap and grouping calls. By creating a list of all passengers on all trips, you can group them, count them, and return the ones that have over a certain number.
Assuming you have data classes like this (essential details only):
data class Passenger(val id: Int)
data class Trip(val passengers: List<Passenger>)
I was able to write this:
fun List<Trip>.frequentPassengers(minTrips: Int): Set<Passenger> =
this
.flatMap { it.passengers }
.groupingBy { it }
.eachCount()
.filterValues { it >= minTrips }
.keys
This is nice because it is a single expression. Going through it, we look at each Trip and extract all of its Passengers. If we had just done map here, we would have List<List<Passenger>>, but we want a List<Passenger> so we flatmap to achieve that. Next, we groupBy the Passenger objects themselves, and call eachCount() on the returned object, giving us a Map<Passenger, Int>. Finally we filter the map down the Passengers we find interesting, and return the set of keys.
Note that I renamed your function, List already has a filter on it, and even though the signatures are different I found it confusing.
You basically want to count the trips for each passenger, so you can put all passengers in a list and then group by them and afterwards count the occurences in each group:
fun List<Trip>.usualPassengers(minTrips : Int) = // 1
flatMap(Trip::passengers) // 2
.groupingBy { it } // 3
.eachCount() // 4
.filterValues { it >= minTrips } // 5
.keys // 6
Explanation:
return type Set<Passenger> can be inferred
this can be ommitted, a list of the form [p1, p2, p1, p5, ...] is returned
a Grouping is created, which looks like this [p1=[p1, p1], p2=[p2], ...]]
the number of occurences in each group will be counted: [p1=2, p2=1, ...]
all elementes with values which less than minTrips will be filtered out
all keys that are left will be returned [p1, p2, ...]
p1...pn are Passenger instances
I have a use case need to sum different feilds in this object so I code like this, can I put it in one stream?
int totalUnits = records.stream()
.mapToInt(DynamoSalesAggregateSummaryRecord::getUnits).sum();
int totalReturns = records.stream()
.mapToInt(DynamoSalesAggregateSummaryRecord::getReturns).sum();
int totalCancellations = records.stream()
.mapToInt(DynamoSalesAggregateSummaryRecord::getCancellations).sum();
BigDecimal totalRevenue = records.stream()
.map(DynamoSalesAggregateSummaryRecord::getRevenue)
.reduce(BigDecimal.ZERO, BigDecimal::add);
BigDecimal totalRoyalties = records.stream()
.map(DynamoSalesAggregateSummaryRecord::getRoyalties)
.reduce(BigDecimal.ZERO, BigDecimal::add);
Honestly, I would not do that. Instead: get rid of the code duplication in the first place: write a little helper that invokes:
return records.stream().mapToInt(accessorMethod).sum();
(where you pass the accessor to that method).
Then you could go for:
int totals = fetch(DynamoSalesAggregateSummaryRecord::getUnits) + fetch(DynamoSalesAggregateSummaryRecord::getReturns) + ...
But beyond that - it is actually not clear what you intend to do here. The above example assumed that you would build a "total" sum of all entries. If that is the not the case - and you want "distinct" sums, my advise would be to give up on using mapToInt(). And instead do something like:
class SumHelper {
private int unitTotals = 0;
private int returnTotals = 0;
...
public add(DynamoSalesAggregateSummaryRecord record) {
unitTotals += record.getUnits();
...
to be used like:
SumHelper helper = new SumHelper;
records.forEach(r -> helper.add(r));
In other words: you want to call multiple different methods on a record; to build different sums. You can't do that with streams - as one operation is creating multiple results. So you need some other kind of "storage" to keep track of the different results.
Another approach would be to create a separate method getTotalSum either in DynamoSalesAggregateSummaryRecord class or (better) - external helper. That method sums up all required fields of a single DynamoSalesAggregateSummaryRecord instance, then just sum it up:
int totalSum =
records.stream()
.mapToInt(DynamoSalesAggregateSummaryRecord::getTotalSum).sum()
java.util.stream.IntStream.sum() is a terminal operation.
You cannot invoke another stream task after a terminal operation.
If you don't have lot of elements in the collection, the GhostCat solution is fine.
If it is not the case, to avoid iterating 5 times on the records collection, another approach should be used.
You could use streams to do it but I am not sure that you will gain in readability as you should probably write your own code to compute aggregates.
A classic loop seems fine in this context :
int totalUnits = 0;
int totalReturns = 0;
...
for (DynamoSalesAggregateSummaryRecord record : records){
totalUnits += record.getUnits();
totalReturns += record.getReturns();
...
}
Ok, So I have a method
public static int getTotalLegCountDog (ArrayList<Dog> dogList)
{
int temp = 0;
for (int i = 0; i < dogList.size(); i++)
{
temp = dogList.get(i).getNumLegs();
totalLegsDogs += temp;
}
return totalLegsDogs;
}
It adds up the total legs of dogs and returns them as totalLegsDogs and there is another that totals the legs for cats.
Now I'd like a method that would take both the returned totalLegsDogs and returned totalLegsCats and add them together. My try is below (It returns 0), any help would be great!
public int getTotalLegCount ()
{
totalLegs = totalLegsDogs + totalLegsCats;
return totalLegs;
}
Was not calling the Method correctly. The math in the Problem was solid. The problem was the Method output call.
As far as I can tell, there's nothing wrong with the methods themselves - likely you're calling getTotalLegCount before actually counting the legs.
Fix 1 (preferred): Have getTotalLegCount call the methods.
public int getTotalLegCount (ArrayList<Dog> dogList, ArrayList<Cat> catList) {
totalLegs = getTotalLegCountDog(dogList) + getTotalLegCountCat;
return totalLegs;
}
Fix 2: Make it very clear that the leg-counting methods are to be called first. This is the inferior solution, as it requires more effort on the future programmer's part (and that might be future-you!).
I don't think you've shown us enough of your code to do any troubleshooting. It looks like you must have a global static count for dog legs and cat legs? I can't figure out your use case, but any rate, you need to make sure both your counting methods are called before you do anything with the member variables or else they will not be initialized. Example:
DogCatCounter.getTotalLegDogCount(...);
DogCatCounter.getTotalLegCatCount(...);
new DogCatCounter().getTotalLegCount();
The result from that third line should be correct as long as no other instances of DogCatCounter have modified your static variables. In other words, if you have multiple instances of DogCatCounter, any calls to your counting methods are going to modify your global static members.
Recursion is a new practice for me and I am trying to get better at it and understand how the methods return. I have the following program but am unfailiar with how to use the this keyword. Can you please review the code and walk me through the program showing the values held by the variables as the methods execute?
I have tried numerous things to determine how the value answer in the compute method holds 14 after execution can anyone walk me through the first few recursive calls so I can try and figure out the rest?
public class Recurs1 {
public static void main (String [] arg) {
Recurs1 r = new Recurs1();
r.compute();
}
public void compute() {
int [] stuff = {1, 2, 3, 4};
int answer = this.go(stuff, 0);
System.out.println("The answer is " + answer);
}
private int go(int[] numbers, int spot) {
if (numbers.length == spot) return spot;
int value = this.go(numbers, spot + 1 );
return value + numbers[spot];
}
}
Ok so a few things I notice here:
The purpose of go() seems to be calculating the sum of the numbers in the array. If this is the case, your method should look like this:
private int go(int[] numbers, int spot) {
if (numbers.length - 1 == spot) return numbers[spot];
int value = this.go(numbers, spot + 1 );
return value + numbers[spot];
}
This is because numbers.length in this case will return 4, but the last element in this array is at index 3 (arrays are 0-indexed).
This way, when the function is called with the second parameter set to 3, it will return the value of the last element in the array and then the code will "bubble up" (as I like to call it) and calculate the sum of the elements by subsequently returning the current summed value + the value of the current call.
As for your problem with the this keyword, it's actually very simple. this always refers to the current class instance your code is in. In this case, you create a Recurs1 instance called r in your main function so whenever you call a method on that particular object, the this keyword used in those methods will refer to r. If you created multiple Recurs1 objects (each with potential different internal states) in your program, their respective this references would always point to themselves allowing you to access their member variables and methods.
Hope that helps and good luck, recursion is usually what most people have trouble getting their heads around at first but once you get used to it it's pretty cool!
OK so this is not an answer to your question per se, more like a lesson in recursion.
Keep in mind I have never tried to to do this with a java class.
Recursion means a function that calls itself repeatedly until a answer has been reached, or your function detects you are running out of stack space.
You first step into the function determines if you will call yourself.
When you call yourself you will push a new copy of the data onto the stack and begin executing. I think in the case of java you will allocate a new object into the heap ( don't quote me on this ) and each invocation will have a new set of variables that get populated with new values.
As you recurse deeper and deeper you simply allocate new copies of the object until you find the answer or run out of memory.
If you find the answer you then return the result to the previous level in the stack of objects eg:
int foo(int i ){
if(some condition){
return foo(i);
} else
return i
}
as You can see if the condition tests true the foo() keeps getting called. Now at each call, the variables of foo() are saved for as many levels deep as you go. If the condition tests false then each instance of foo() returns to the previous until you are at the original invocation of foo() which then returns to the caller of foo().
Clear as Mud?