Java input from text file with an unusual format - java

So I realize this text format may not be very unusual. However, I've been trying many ideas to read this correctly into the objects needed and know there has to be a better way. Here is what the file looks like:
S S n
B 1 E 2
B N n
C 2 F 3
C N n
D 2 GA 4
D N n
GA 1
E N n
B 1 F 3 H 6
F N n
I 3 GA 3 C 1
A N g
H B b
U 2 GB 2 F 1
I N n
GA 2 GB 2
GB N g
So the first line of each pair is the name of the node S, whether its a starting node N/s then whether its a goal node g/n
The second line is the children of node S, and their distance weight.
For example, Node S has child node B with a distance of 1, and child node E with a distance of 2
I'm working with two object types, Nodes, and Edges.
Example below. (Constructors ommitted)
Does anyone have tips on how to read this type of input efficiently?
public class Edge {
public String start;
public String end;
public int weight;
public class Node {
public String name;
public boolean start = false;
public boolean goal = false;
public ArrayList<Edge> adjecentNodes = new ArrayList<Edge>();

Actually your question is almost too broad and unspecific, but I am in the mood to give you some starting points. But please understand that you could easily fill several hours of computer science lectures on this topic; and that is not going to happen here.
First, you have to clarify some requirements (for yourself; or together with the folks working on this project):
Do you really need to focus on efficient reading/building of your graph? I rather doubt that: building the graph happens once; but the computations that you probably do later on may run for a much longer time. So one primary focus should be on designing that object/class model that allows you to efficiently solve the problems that you want to solve on that graph! For example: it might be beneficial to already sort edges by distance/weight when creating the graph. Or maybe not. Depends on later use cases! And even when you are talking about huge files that need efficient processing ... that still means: you are talking about huge graphs; so all the more reason to find a good model for that graph.
Your description of the file is not clear. For example: is this a (un)directed graph? Meaning - can you travel on any edge in both direction? And sorry, I didn't get what a "goal" node is supposed to be. (I guess you have directed edges that go one way only, as that would explain those rows in the example where nodes do not have any children). Of course, sometimes requirements become clear in that moment when you start writing real code. But this here is really about concepts/data/algorithms. So the earlier you answer all such questions, the better for you.
Secondly, a suggestion in which order to do things:
As said, clarify all your requirements. Spend some serious time just thinking about the properties of the graphs you are dealing with; and what problems you later have to solve on them.
Start coding; ideally you use TDD/unit testing here. Because all of the things you are going to do can be nicely sliced into small work packages, and each one could be tested with unit-tests. Do not write all your code first, to then, after 2 days running your first experiments! The first thing you code: your Node/Edge classes ... because you want to play around with things like: what arguments do my constructors need? how can I make my classes immutable (data is pushed in by constructors only)? Do I want distance to be a property of my Edge; or can I just go with Node objects (and represent edges as Map<Node, Integer> --- meaning each node just knows its neighbors and the distance to get there!)
Then, when you are convinced that that Node/Edge fit your problem, then you start writing code that takes strings and builds Node/Edges out of those strings.
You also went to spent some time on writing good dump methods; ideally you call graph.dump() ... and that produces a string matching your input format (makes a nice test later on: reading + dumping should result in identical files!)
Then, when "building from strings" works ... then you write the few lines of "file parsing code" that uses some BufferedReader, FileReader, Scanner, Whatever mechanism to dissect your input file into strings ... which you then feed into the methods you created above for step 3.
And, seriously: if this is for school/learning:
Try to talk to your peers often. Have them look at what you are doing. Not too early, but also not too late.
Really, seriously: consider throwing away stuff; and starting from scratch again. For each of the steps above, or after going through the whole sequence. It is an incredible experience to do that; because typically, you come up with new, different, interesting ideas each time you do that.
And finally, some specific hints:
It is tempting to use "public" (writable) fields like start/end/... in your classes. But: consider not doing that. Try to hide as much of the internals of your classes. Because that will make it easier (or possible!) later on to change one part of your program without the need to change anything else, too.
Example:
class Edge {
private final int distance;
private final Node target;
public Edge(int distance, Node target) {
this.distance = distance; this.target = target;
}
...
This creates an immutable object - you can't change its core internal properties after the object was created. That is very often helpful.
Then: override methods like toString(), equals(), hashCode() in your classes; and use them. For example, toString() can be used to create a nice, human-readable dump of a node.
Finally: if you liked all of that, consider remembering my user id; and when you reach enough reputation to "upvote", come back and upvote ;-)

Related

Is there a better method for randomizing functions besides Random?

I have 2 strings in an array. I want there to be a 10% chance of one and 90% chance to select the other. Right now I am using:
Random random = new Random();
int x = random.nextInt(100 - 1) + 1;
if (x < 10) {
string = stringArray(0);
} else {
string = stringArray(1);
}
Is this the best way of accomplishing this or is there a better method?
I know it's typically a bad idea to submit a stack overflow response without submitting code, but I really challenge this question of " the best way." People ask this all the time and, while there are established design patterns in software worth knowing, this question almost always can be answered by "it depends."
For example, your pattern looks fine (I might add some comments). You might get a minuscule performance increase by using 1 - 10 instead of 1 - 100, but the things you need to ask yourself are as follows :
If I get hit by a bus, is the person who is going to be working on the application going to know what I was trying to do?
If it isn't intuitive, I should write a comment. Then I should ask myself, "Can I change this code so that a comment isn't necessary?"
Is there an existing library that solves this problem? If so, is it FOSS approved (if applicable) / can I use it?
What is the size of this codebase eventually going to be? Am I making a full program with microservices, a DAO, DTO, Controller, View, and different layers for validation?
Is there an existing convention to solve my problem (either at my company or in general), or is it unique enough that I can take my own spin on it?
Does this follow the DRY principle?
I'm in (apparently) a very small camp on stack overflow that doesn't always believe in universal "bests" for solving code problems. Just remember, programming is only as hard as the problem you're trying to solve.
EDIT
Since people asked, I'd do it like this:
/*
* #author DaveCat
* #version 1.0
* #since 2019-03-9
* Convenience method that calculates 90% odds of A and 10% odds of B.
*
*/
public static String[] calculatesNinetyPercent()
{
Random random = new Random();
int x = random.nextInt(10 - 1 ) + 1
//Option A
if(x <= 9) {
return stringArray(0);
}
else
{
//Option B
return stringArray(1);
}
}
As an aside, one of the common mistakes junior devs make in enterprise level development is excessive comments.This has a javadoc, which is probably overkill, but I'm assuming this is a convenience method you're using in a greater program.
Edit (again)
You guys keep confusing me. This is how you randomly generate between 2 given numbers in Java
One alternative is to use a random float value between 0..1 and comparing it to the probability of the event. If the random value is less than the probability, then the event occurs.
In this specific example, set x to a random float and compare it to 0.1
I like this method because it can be used for probabilities other than percent integers.

Count how many list entries have a string property that ends with a particular char

I have an array list with some names inside it (first and last names). What I have to do is go through each "first name" and see how many times a character (which the user specifies) shows up at the end of every first name in the array list, and then print out the number of times that character showed up.
public int countFirstName(char c) {
int i = 0;
for (Name n : list) {
if (n.getFirstName().length() - 1 == c) {
i++;
}
}
return i;
}
That is the code I have. The problem is that the counter (i) doesn't add 1 even if there is a character that matches the end of the first name.
You're comparing the index of last character in the string to the required character, instead of the last character itself, which you can access with charAt:
String firstName = n.getFirstName()
if (firstName.charAt(firstName.length() - 1) == c) {
i++;
}
When you're setting out learning to code, there is a great value in using pencil and paper, or describing your algorithm ahead of time, in the language you think in. Most people that learn a foreign language start out by assembling a sentence in their native language, translating it to foreign, then speaking the foreign. Few, if any, learners of a foreign language are able to think in it natively
Coding is no different; all your life you've been speaking English and thinking in it. Now you're aiming to learn a different pattern of thinking, syntax, key words. This task will go a lot easier if you:
work out in high level natural language what you want to do first
write down the steps in clear and simple language, like a recipe
don't try to do too much at once
Had I been a tutor marking your program, id have been looking for something like this:
//method to count the number of list entries ending with a particular character
public int countFirstNamesEndingWith(char lookFor) {
//declare a variable to hold the count
int cnt = 0;
//iterate the list
for (Name n : list) {
//get the first name
String fn = n.getFirstName();
//get the last char of it
char lc = fn.charAt(fn.length() - 1);
//compare
if (lc == lookFor) {
cnt++;
}
}
return cnt;
}
Taking the bullet points in turn:
The comments serve as a high level description of what must be done. We write them aLL first, before even writing a single line of code. My course penalised uncommented code, and writing them first was a handy way of getting the requirement out of the way (they're a chore, right? Not always, but..) but also it is really easy to write a logic algorithm in high level language, then translate the steps into the language learning. I definitely think if you'd taken this approach you wouldn't have made the error you did, as it would have been clear that the code you wrote didn't implement the algorithm you'd have described earlier
Don't try to do too much in one line. Yes, I'm sure plenty of coders think it looks cool, or trick, or shows off what impressive coding smarts they have to pack a good 10 line algorithm into a single line of code that uses some obscure language features but one day it's highly likely that someone else is going to have to come along to maintain that code, improve it or change part of what it does - at that moment it's no longer cool, and it was never really a smart thing to do
Aominee, in their comment, actually gives us something like an example of this:
return (int)list.stream().filter(e -> e.charAt.length()-1)==c).count();
It's a one line implementation of a solution to your problem. Cool huh? Well, it has a bug* (for a start) but it's not the main thrust of my argument. At a more basic level: have you got any idea what it's doing? can you look at it and in 2 seconds tell me how it works?
It's quite an advanced language feature, it's trick for sure, but it might be a very poor solution because it's hard to understand, hard to maintain as a result, and does a lot while looking like a little- it only really makes sense if you're well versed in the language. This one line bundles up a facility that loops over your list, a feature that effectively has a tiny sub method that is called for every item in the list, and whose job is to calculate if the name ends with the sought char
It p's a brilliant feature, a cute example and it surely has its place in production java, but it's place is probably not here, in your learning exercise
Similarly, I'd go as far to say that this line of yours:
if (n.getFirstName().length() - 1 == c) {
Is approaching "doing too much" - I say this because it's where your logic broke down; you didn't write enough code to effectively implement the algorithm. You'd actually have to write even more code to implement this way:
if (n.getFirstName().charAt(n.getFirstName().length() - 1) == c) {
This is a right eyeful to load into your brain and understand. The accepted answer broke it down a bit by first getting the name into a temporary variable. That's a sensible optimisation. I broke it out another step by getting the last char into a temp variable. In a production system I probably wouldn't go that far, but this is your learning phase - try to minimise the number of operations each of your lines does. It will aid your understanding of your own code a great deal
If you do ever get a penchant for writing as much code as possible in as few chars, look at some code golf games here on the stack exchange network; the game is to abuse as many language features as possible to make really short, trick code.. pretty much every winner stands as a testament to condense that should never, ever be put into a production system maintained by normal coders who value their sanity
*the bug is it doesn't get the first name out of the Name object

Is there a way to compare two methods by function rather than value? [duplicate]

This question already has answers here:
Is finding the equivalence of two functions undecidable?
(9 answers)
Closed 6 years ago.
Is there a way to compare if two methods are equivalent by function (i.e. they do the same thing) rather than equivalent by value (i.e. all of the code in the method is the same) ?
For example these two methods are coded differently, but perform the same function.
public int doIt(int a, int b) {
a = a + 1;
b = b + 1;
return a + b;
}
public int doIt2(int z, int x) {
int total = z + x + 2;
return total;
}
I was looking for a way to do this in Eclipse, but am interested if this is even possible beyond a trivial method.
The only way to be 100% is to mathematically prove it
There are ways:
1- Theorem proving
2- Model Checking
and etc
Although these approaches can be very hard, sometime it might take days to prove it even for trivial programs and even days to produce the adequate abstraction level.
There are some heuristic approaches but obviously they are not 100% accurate (heuristic)
A simple heuristic approach would be to try both methods for 1000 inputs and see if the results are the same
EDIT:
here is a list of Model Checker I found on Wikipedia. I haven't used any of them, they may not be exactly what you are looking for.
https://en.wikipedia.org/wiki/List_of_model_checking_tools
Ignoring side effects, 2 functions will be functionally equivalent if for the same input, they produce the same output.
This will only work for pure code though. There's no way I know of to monitor for side effects in general since the side effects a function carries out could be anything.
Note, there wouldn't be a way to completely verify this without testing every possible input. If the input is just a limited Enum, that might be easy. If it's 2 integers though for example, the total number of combinations would be huge.
In general, the purpose of refactoring is to have a function behave the same before and after it is refactored. Developers generally do this by creating extensive unit tests, testing both normal, edge, and exception cases.
In the OP's two functions to be compared, doIt and doIt2, they might usually return the same answer, given any integer inputs a and b. Unit testing would demonstrate this.
But what if a or b were the largest integer that Java could store, MAX_VALUE?
What if there were a side effect from a=a+1?
In these cases, the two functions may appear similar on the surface, but yield different results.

How to handle the case that a single simple test case will drive the whole implementation?

When I learn "Test Driven Development", I found an interesting case from the book "The Productive Programmer":
You need to find all factors of a "complete number". A complete number is the sum of all its factors (except the one which equals to the number itself) is equal to the number. So 6 is the minimal complete number, and its factors are 1, 2, 3.
If I want to TDD, first I want to test an simplest test case:
#Test public void completeNumber6() {
CompleteNumber completeNumber = new CompleteNumber(6)
assertEquals(completeNumber.findFactors(), new Int[] {1,2,3});
}
But ! The problem is this simplest case will driven all the implementation of findFactors(), which seems too much for me.
The author gives some suggestions, we can split the requirements into several steps:
check if number is the factor of another one
provide a way to collect some factors into a collection
check each smaller number to see if it's the factor of the given number, collect them
check if the sum of the collected factor equal to the given number
And we can TDD the first 2 steps first:
#Test public void testIsFactor() {}
#Test public void testAddFactor() {}
So there will be 2 public (at least non-private) methods after that:
boolean isFactor(Int n1, Int n2)
void addFactor(Int factor)
The problem is these 2 methods should be private after the whole implementation, since they should only used by findFactors internally!
But if they are changed to private, what shall we do with the exsiting test cases for them?
The author suggests we can change them to private, and use Java refection API to get and test them. Sounds possible, but I'm not sure if it's a good practice to do so.
I also asked some friends, and they gave some other options:
Keep the methods isFactor and addFactor non private as is, that's acceptable
Extract a class FactorChecker and FactorCollector for the 2 methods
Change them to private, and delete the test cases since the functionality of them has been tested in the later test cases (for step 3 & 4)
I'm really puzzled now, which approach is the best practice of TDD?
Seems to me that the fact that the question states these are complete numbers is somewhat irrelevant. You can calculate the factors of any whole number. So given that I'd start with implementing findFactors(1) and then work my way up.
That makes this a slight variation on the classic Prime Factors Kata, the only difference being that you add a 1 to list of factors.

Problem with terminated paths in simple recursive algorithm

First of all: this is not a homework assignment, it's for a hobby project of mine.
Background:
For my Java puzzle game I use a very simple recursive algorithm to check if certain spaces on the 'map' have become isolated after a piece is placed. Isolated in this case means: where no pieces can be placed in.
Current Algorithm:
public int isolatedSpace(Tile currentTile, int currentSpace){
if(currentTile != null){
if(currentTile.isOpen()){
currentTile.flag(); // mark as visited
currentSpace += 1;
currentSpace = isolatedSpace(currentTile.rightNeighbor(),currentSpace);
currentSpace = isolatedSpace(currentTile.underNeighbor(),currentSpace);
currentSpace = isolatedSpace(currentTile.leftNeighbor(),currentSpace);
currentSpace = isolatedSpace(currentTile.upperNeighbor(),currentSpace);
if(currentSpace < 3){currentTile.markAsIsolated();} // <-- the problem
}
}
return currentSpace;
}
This piece of code returns the size of the empty space where the starting tile is part of. That part of the code works as intented. But I came across a problem regarding the marking of the tiles and that is what makes the title of this question relevant ;)
The problem:
The problem is that certain tiles are never 'revisited' (they return a value and terminate, so never get a return value themselves from a later incarnation to update the size of the empty space). These 'forgotten' tiles can be part of a large space but still marked as isolated because they were visited at the beginning of the process when currentSpace had a low value.
Question:
How to improve this code so it sets the correct value to the tiles without too much overhead? I can think of ugly solutions like revisiting all flagged tiles and if they have the proper value check if the neighbors have the same value, if not update etc. But I'm sure there are brilliant people here on Stack Overflow with much better ideas ;)
Update:
I've made some changes.
public int isolatedSpace(Tile currentTile, int currentSpace, LinkedList<Tile> visitedTiles){
if(currentTile != null){
if(currentTile.isOpen()){
// do the same as before
visitedTiles.add();
}
}
return currentSpace;
}
And the marktiles function (only called when the returned spacesize is smaller than a given value)
marktiles(visitedTiles){
for(Tile t : visitedTiles){
t.markAsIsolated();
}
}
This approach is in line with the answer of Rex Kerr, at least if I understood his idea.
This isn't a general solution, but you only mark spaces as isolated if they occur in a region of two or fewer spaces. Can't you simplify this test to "a space is isolated iff either (a) it has no open neighbours or (b) precisely one open neighbour and that neighbour has no other open neighbours".
You need to have a two-step process: gathering info about whether a space is isolated, and then then marking as isolated separately. So you'll need to first count up all the spaces (using one recursive function) and then mark all connected spaces if the criterion passes (using a different recursive function).

Categories

Resources