Related
in short, the question is, I want to delete the last element of the DOUBLY LINKED LIST and I wrote this function...
fun deleteLast(){
if(isEmpty()){println("list is empty")}
if (head!=null && head?.nextNode == null){
head = null
}else {
tail = tail?.previousNode
tail?.nextNode = null
}
}
I went to the tail without traversing it and made its previous as new tail and new tail next(old tail) as null. And my result is desirable.
after writing this I pinged Google to check if this is right so, I found this...
void pop_back() {
if(this.head != null) {
//1. if head in not null and next of head
// is null, release the head
if(this.head.next == null) {
this.head = null;
} else {
//2. Else, traverse to the second last
// element of the list
Node temp = new Node();
temp = this.head;
while(temp.next.next != null)
temp = temp.next;
//3. Change the next of the second
// last node to null and delete the
// last node
Node lastNode = temp.next;
temp.next = null;
lastNode = null;
}
}
}
I want to ask If my code is right I just want you masters to review it. Thanks in advance!
The question only contains a snippet, not a minimal, reproducible example, so for the sake of argument (based on existing code and the comment) I'm going to assume that the full code looks something like this:
class Node<T>(var previousNode: Node<T>?, var nextNode: Node<T>?, var value: T)
class DoublyLinkedList<T>(var head: Node<T>?, var tail: Node<T>?) {
fun isEmpty() = head == null
// …other methods…
fun deleteLast(){
if(isEmpty()){println("list is empty")}
if (head!=null && head?.nextNode == null){
head = null
}else {
tail = tail?.previousNode
tail?.nextNode = null
}
}
}
Given this, it looks like your code is about right. There are a few points that could be addressed*, e.g.:
Inside the second if I think it needs to set tail to null as well, for safety.
Inside the second and third branches, you might want to set to null the previousNode and nextNode references in the node you're removing, for the benefit of any other code that still has a reference to it. (That might also give an extra hint to the garbage collector, though that shouldn't be needed.)
Instead of calling isEmpty(), it'd be simpler and more consistent to check whether head is null.
I'd add an else before the second if. As it is, the code works for an empty list, but that may be just luck; it'd be much more robust if neither of the other branches could be called on an empty list. (In some cases, this could let the compiler smart-cast head to non-nullable; but that doesn't happen here because it's mutable.)
In production code, you wouldn't print to stdout like that. If it's allowed to call deleteLast() on an empty list, then there's no need to print anything; if not (which would be more usual), you'd throw an exception. (And the method would have a doc comment explaining that.)
The formatting could be improved.
(* In my experience, any code can be improved — even code you've worked on many times before!)
However, you can't compare your code directly to the Google-sourced code, because it looks like that is for a singly-linked list.
A singly-linked list is a much simpler structure. All it has is a reference to the head node; that has a reference to the next node (and, presumably to the data stored in the node); and so on. So the only way to reach the end of the list is to traverse all the nodes, as the Google-sourced code does. (This also means there's often no point in having a separate class to represent the list as a whole; a reference to the first node is all you need. Many operations can be written concisely with recursive methods. You can also make it immutable, which has many advantages; that's the primary data structure in some languages.)
Your list, though, has a direct reference to the last node, which of course avoids the need to traverse the entire list to find it. As your code demonstrates, this means that some operations are much more efficient in double-linked lists. There are corresponding disadvantages, though: each node takes more memory; there are more references to update when changing the list; it's easier for the list to get into an inconsistent state; and it's less suitable for an immutable list.)
In Kotlin, how do you branch on multiple conditions, evaluated lazily, some of which may involve variable assignment?
For example, here is a C++ version of this scenario:
int b;
if (a > 0 && (b = getB()) > 0)
branch1(a, b);
else
branch2();
Here is a Swift version, assuming a guard scenario where return is acceptable. Using Optional is a bit hacky, but it works:
guard a > 0, let b = Optional(getB()), b > 0
else { branch2(); return }
branch1(a, b)
In the examples, branch1 and branch2 are function calls for explanatory purposes, but may be inline code in real-world use. Having to factor that code into separate methods may be undesirable.
Kotlin, like Swift, has assignments that don't evaluate to the assigned value. However, Kotlin doesn't have a direct equivalent to guard, which supports assignment with multiple lazy conditions. What is an eloquent way in Kotlin to lazily evaluate a condition that includes an assignment?
I don't think there's an elegant solution to this sort of construct; here are some uglier options.
Perhaps the most direct translation would be along the lines of:
val a: A
if (b && r != null && getA()?.also{ a = it } != null)
branch1(b, r, a) // ERROR: Variable 'a' must be initialized
else
branch2()
…except that the compiler isn't (currently) smart enough to see that a will always have been assigned by the time it's used in branch1().
You could work around that by making it a var, and giving it an initial value — but the only likely value is null, which means making the field nullable and then having to add !! when you use it. All of which makes it rather ugly — even though the condition is in one place, and getA() is called only if needed:
var a: A? = null
if (b && r != null && getA()?.also{ a = it } != null)
branch1(b, r, a!!)
else
branch2()
A variation might be to split the condition, and always assign a:
val a = if (b && r != null) getA() else null
if (a != null)
branch1(b, r!!, a)
else
branch2()
That works — but now the compiler can't tell that r is non-null, so you need !! there instead.
You could make that slightly simpler with a when (though you need to flip the cases):
when (val a = if (b && r != null) getA() else null) {
null -> branch2()
else -> branch1(b, r!!, a)
}
Ultimately, the problem is that you're trying to get side-effects that only happen under some circumstances, and then use those side-effects back in the condition and the resulting branch — and that's as confusing for humans as it is for the compiler. (C was my main language for many years, and I always found embedded assignments awkward…) If I saw such code, I'd consider rewriting it in a more straightforward way, for the benefit of whoever had to maintain it. In this case, that might be:
if (b && r != null) {
val a = getA()
if (a != null)
branch1(b, r, a)
else
branch2()
} else
branch2()
This is longer-winded, and has to duplicate the branch2() call (though there might be ways around that if the call were very complicated, e.g. by setting a flag). But it's arguably easier to follow (especially as regards seeing when getA() might be called); a doesn't need to be mutable; and the compiler can see that both r and a cannot be null in the branch1() call, and so smart-casts them.
(By the way, all of these assume that getA() could be expensive and/or have side-effects; if not, there would be simpler alternatives that called it unnecessarily. Conversely, if more than one assignment were needed, all of these options would get more complicated.)
Just learned Kotlin Nullable type and let{} function which replaces the if (xx != null) {} operation.
But one thing I am confused is that, we all know and I Think the Complier Should Know that when we use let{}, the variable/object who is calling this function is possiblly null, however the complier still requires me to add the safe call operator "?" after the variable name instead of providing Smart Cast like it does in if (xx != null) {}. Why?
My piece of code:
fun main() {
var number1: Int? = null
//val number2 = number1.let { it + 1 } ?: 10 //doesn't work, not quite "smart"
val number2 = number1?.let { it + 1 } ?: 10 //works, must have "?"
println(number1)
println(number2)
}
You've already got answers in the comments, but just to explain the ? thing...
Kotlin lets you make null-safe calls on nullable variables and properties, by adding ? before the call. You can chain this too, by doing
nullableObject?.someProperty?.someFunction()
which evaluates nullableObject, and if it's non-null it evaluates the next bit, otherwise the whole expression evaluates to null. If any part of the chain evaluates as null, the whole expression returns null.
So it has this short-circuiting effect, and you can use the elvis "if null" operator to create a default value if you can't evaluate the whole chain to a non-null result:
nullableObject?.nullableProperty?.someFunction() ?: defaultAction()
and once you introduce the null check in the chain, you have to add it for every call after that - it's basically propagating either the result of the previous bit, or the null it resolved to, so there's a null check at each step
The let block is just a scope function - you use it on a value, so you can run some code either using that value as a parameter or a receiver (a variable or this basically). It also has the side effect of creating a new temporary local variable holding that value, so if the original is a var it doesn't matter if that value changes, because your let code isn't referring to that variable anymore.
So it's useful for doing null checks one time, without worrying the underlying value could become null while you're doing stuff with it:
nullableVar?.let { it.definitelyIsNotNull() }
and the compiler will recognise that and smart cast it to a non-null type. An if (nullableVar != null) check can't guarantee that nullableVar won't be null by the time the next line is executed.
In a beanshell program, I get a value in a variable. Now, if there was no value obtained for this variable, then I want to set this as "Missing"
In a very old version of Beanshell I used the command as shown below to do this--
contact.toString() != ''
However I updated the Beanshell library and now I am getting an error message that says that I cannot put a second apostrophe after the first one (i.e. '' is erroneous).
How do I check correctly for the above condition in Beanshell?
An apostrophe is used in the Java language to indicate a char. You cannot have a '' char value. However the earlier version of beanshell may have been doing some implicit conversion to an empty string that somehow is broken after upgrade.
Checking for a valid value using the toString() seems a bit wasteful to me, but if 'contact' be non-null, yet still not have a value in the toString() representation the you check the String value as follows:
if(contact != null) {
contactStr = contact.toString();
if(contactStr != null #and contactStr.length() > 0) {
/// value is something other than null or ""
}
}
and the toString() is the only way
If you want to check whether contact is defined then you do:
if (contact == void)
http://www.beanshell.org/manual/specialvarsvalues.html
Which one of the following do you do:
var = true;
if (...) var = false;
Or
if (...) var = false;
else var = true;
Is there a reason you pick on or the other?
I'm working on the premise that nothing else is happening to var. The next line of code might be something like:
if (var) { ... }
How about var = { ... } directly since it's a boolean?
I prefer the second in Java, doing something like this:
int x;
if (cond) {
x = 1;
} else {
x = 5;
}
because if something is changed later (for example, I turn the else block into an else if), the compiler will tell me that the variable has failed to be initialized, which I might miss if I used your first strategy.
You could also use a ternary operator if your language supports it :)
I would generally only do the first one if there was a chance the IF could fail and the variable must have a default value if it does.
If you set the default, then you reset it again later to something else, although it's a very small amount, its still a waste of resources. So, most of the time, for most of the code, a balanced if/else or even a (?:) syntax, are clearer and more appropriate, except:
Sometimes, if what you doing is building fall-through code (or a decision function), where you start with a specific condition, and then test a whole bunch of other conditions to see if that changes, then you want to definitely set the default first:
int final = 27;
if ( some condition ) final = 86;
if ( another condition ) {
final = 98;
return final;
}
if ( some state ) {
final += 2;
}
return final;
Or something similar to that.
BTW: in your example, if you set 'var', then the next line just tests 'var', you don't really need 'var' do you? If the condition is so ugly that using 'var' helps make it readable, then your probably best to move the condition into it's own function, accepting that the extra function call is there to help readability. In general, you can waste resources, if and only if you get something significant, such as readability, in return.
Paul.
Depends on the context. I would use the second option when it is clear that 'var' needs to be true when IF fails.
I use the first type unless the value to set requires significant computation.
Always the first as many people have said. However it's worth emphasising why, and that's because it makes the program more resistant to future bugs caused by incorrect maintenance.
For example, it's quite common for some additional business condition to arise and a maintenance coder add some extra condition or two inside the if to include more business logic and incorrectly amend the code - for example
if (a==b) {
if (a==c) {
[new logic]
var=false
}
}
else {
var = false
}
On the face of it it looks unlikely, but it happens alarmingly often (in fairness often the situation arises after the original if has got a lot more complex). Putting the initialisation first prevents this.
Do you prefer code that is short and compact, or code that is easier to read?
If you prefer code that is short and compact use
var x = true;
if (...) x = false;
But this can even be "improved". Most languages give initial values, and usually for the boolean type the default is false. So, you could write
var x;
if (...) x = true;
If you prefer code that is easy to read use
if (...) var x = false;
else var x = true;
because it makes your intentions clear.
The performance of both is the same.
Depends on the language. In C++, I would highly recommend setting it to a default as quickly as possible otherwise you risk getting garbage results.
In most other languages, you can be a bit more flexible. In fact, I would argue that it's more readable to explicitly define the conditions than to set a default.
Since the variable is not written to later, for general values I would write the following in Java:
final Type var;
if (cond)
{
var = value1;
}
else
{
var = value2;
}
The Java compiler will catch the error that var is not assigned a value before it is used.
The final keyword expresses the fact that the variable is constant after the conditional.
In your exact case with booleans I would use
final boolean var = !cond;
Using a conditional in this case indicates you are afflicted by "booleanophobia".
In C I would initialize the variable at its declaration.
I generally set the "default" value and use if statements to modify it.
If no default exists then only the if statements.
int timeout = 100;
if (moreTime) timeout = 1000;
int searchOption = null;
if (sometest1) searchOption = 1;
if (sometest2) searchOption = 2;
// then later..
if (searchOption != null)
.....
If the initialization is complex enough that a direct expression can't be cleanly written, I sometimes find it useful to handle this case as
boolean isFoo = determineWhetherFoo(...);
where determineWhetherFoo takes whatever arguments are necessary to make the determination and returns the appropriate boolean result. This makes it very clear what the variable means and what it depends on. Initializing a variable to a possibly-wrong value, followed by a wad of code that may change its value, can sometimes obscure what's being expressed.
Wherever you write an if() also write the else - even if it's empty.
The compiler will optimise it away but it forces you (and any programmers after you) to think about when the if () isn't triggered, what are the consequences?