how to implement linked list deletion? - c

#include<stdio.h>
struct node {
int info;
struct node *next;
};
typedef struct node *nodeptr;
nodeptr i;
nodeptr q;
nodeptr p;
nodeptr *plist;
nodeptr getnode(void)
{
nodeptr p;
p = (nodeptr) malloc(sizeof(struct node));
return p;
}
void freenode(nodeptr p)
{
free(p);
}
int main()
{
int i;
nodeptr *k;
int a;
int *px;
int r;
nodeptr end;
nodeptr s;
nodeptr start;
p = getnode();
q = getnode();
q = start;
p = end;
for (i = 0; i < 6; i++) {
printf("enter value");
scanf("%d", &r);
p = getnode();
p->info = r;
q->next = p;
q = q->next;
}
q = start;
while ((q->next) != NULL) {
printf("n%d", (q->next)->info);
q = q->next;
}
scanf("%d", &a);
end = getnode();
end->info = a;
end->next = NULL;
for (q = start; q->next != NULL; q = q->next)
;
q->next = end;
q = start;
while ((q->next) != NULL) {
printf("n%d", (q->next)->info);
q = q->next;
}
for (q = start; q->next->next != NULL; q = q->next)
;
freenode(q->next);
q->next = NULL;
q = start;
while (q->next != NULL) {
printf("n%d", (q->next)->info);
q = q->next;
}
return 0;
}
in this program alist is made and an element is inserted at the end
in this the element is getting deleted but the list is not shown properly
only last two elements are getting displayed
please help so as to show the whole list with element deleted

There is a lot wrong, undortunately. As WhozCraig said, there's a lot of other posts on this topic, so you should have probably searched a little more before posting. But since you have, let's walk through some of the issues together.
nodeptr i;
nodeptr q;
nodeptr p;
nodeptr *plist;
Here you are declaring a ton of global variables, most with bad names. What's i? What's p? What's q? Further down, you redeclare variables with the same names. Some with the same type, others with different type. This make it confusing to know which variable you're referencing.
Generally speaking, avoid global variables and choose descriptive names. In this case, you can just get rid of i, p and q.
Also, you never initialize plist to anything; you should get into the habit of initializing variables to some sane default value. In this case, NULL could be appropriate, but since you do not use the variable at all it can be deleted.
nodeptr getnode(void)
{
nodeptr p;
p = (nodeptr) malloc(sizeof(struct node));
return p;
}
This is good, however in C you should not cast the result of malloc to a particular type as this is considered bad form and can cause subtle and hard to detect bugs. Just assign the return from malloc directly.
Secondly, you never check to make sure that malloc succeeded. Granted, it's unlikely that it would fail in your simple program, but you should get in the habit of checking the return value of functions that can fail.
And you should probably initialize the allocated memory to some default value, because the memory returned to you by malloc is full of junk. In this case, something like this seems appropriate:
if(p) /* only if we allocated memory. */
memset(p, 0, sizeof(struct node));
There are times when you could skip this, but clearing the memory is a sane default practice.
void freenode(nodeptr p)
{
free(p);
}
This is also fine, but you should consider verifying that p is not NULL before you call free. Again, this comes down to robustness, and it's a good habit to get into.
int main()
{
int i;
nodeptr *k;
int a;
int *px;
int r;
nodeptr end;
nodeptr s;
nodeptr start;
Again, here we have a lot of unitialized variables, but at least some of the names are a bit better. But notice what happens:
You declare a variable called i of type int. But you've already declared a global variable called i that is of type nodeptr. So now, the variable in the local scope (the int) shadows (that is, it hides it) the global variable. So inside main the name i refers to the int. This just adds to the confusion when someone is reading your program.
p = getnode();
q = getnode();
OK... so, over here you allocate two new nodes and make p and q point to those nodes. So far so good.
q = start;
p = end;
Oops... now this is a problem. We now make p and q point to wherever start and end respectively point to.
And where do those point to? Who knows. Both start and end are unitialized, so they can point to anything. From this point on, your program exhibits undefined behavior: this means that anything can happen. Most likely, in this case, it will just crash.
Unfortunately from here on down, things become a lot more confusing. Instead of trying to explain everything, I'll just give some general commentary.
for (i = 0; i < 6; i++) {
printf("enter value");
scanf("%d", &r);
p = getnode();
p->info = r;
q->next = p;
q = q->next;
}
This loop is supposed to read 6 integers and put them in our linked list. This seems like a simple thing to do but there are issues.
First of all, you never check the return of scanf to know if the input operation succeeded. As I said before, you should always check the return value of functions that can fail and handle the failure accordingly. But in this case, let's ignore that rule.
A big problem is that q points to a random location in memory. So we're in undefined behavior land.
Another big problem is that there are two cases to consider: when the list is empty (i.e. the first time we're adding a number to the list when i == 0) and when the list is not empty (i.e. every other time). The behavior in those two cases differs. When i == 0 we can't just blindly set q->next, because even if q didn't point to a random location, there would, conceptually, be no q the way it's used here.
What we need here is some extra logic: if this is the first node that we are creating, set q to point to that node. Otherwise, set q->next to that node and then do q = q->next.
Please note, also, that you never set p->next anywhere, which would cause your list to not be NULL-terminated (something that you rely on here and in other loops). The memset fix in getnode corrects this problem, but generally you should make sure that if your code expects a particular behavior ("an unlinked node's next pointer points to NULL; lists are NULL-terminated") you should have code to ensure that behavior.
q = start;
Again, here, we reset q to point to start which is still uninitialized and points to garbage.
while ((q->next) != NULL) {
printf("n%d", (q->next)->info);
q = q->next;
}
This is a classic printing loop. Nothing wrong here, per se, although I think that stylistically, those parentheses around q->next are overkill and make reading the code a little more difficult than it has to be. My guideline would be to only add parentheses when they're necessary to override the default evaluation order of C or when the parentheses helps to visually explain to the reader how to group the expression in his head when mentally parsing the code.
scanf("%d", &a);
end = getnode();
end->info = a;
end->next = NULL;
This is fine, except for the error-checking issue with scanf, although you don't prompt the user to enter a number. But you correctly and explicitly make end->next point to NULL which is great.
for (q = start; q->next != NULL; q = q->next)
;
Again, the problem here is that q is set to start which, unfortunately, still points to garbage.
q->next = end;
q = start;
while ((q->next) != NULL) {
printf("n%d", (q->next)->info);
q = q->next;
}
This is the second time you've had to type this code to print the list. Generally, you should avoid code duplication. If you find that you need a particular code block in more than one place, it makes sense to split it out into a function and use the function. This makes understanding and maintaining the code easier.
for (q = start; q->next->next != NULL; q = q->next)
;
This loop is tricky to understand because of the q->next->next bit. Ask yourself "if I'm reading this, am I immediately sure that q->next can't ever be NULL?" If you aren't, then you really ought to rewrite this loop.
freenode(q->next);
q->next = NULL;
q = start;
Again, q is made to point to start which is unitialized. But hey, if we haven't crashed yet... ;)
while (q->next != NULL) {
printf("n%d", (q->next)->info);
q = q->next;
}
And again... this should really be a function.
return 0;
}
For a better implementation I refer you to one of the many other questions asked here (just search for "linked list delete". The implementation in this question by Khalid Waseem could also be of help, but it's not very documented, so you will have to carefully study and analyze the code to make sure that you understand it.

See below implementation for a better understanding.
struct node {
int info;
struct node *next;
};
typedef struct node node;
//Function to print a given single linked list.
void print_list(node *start)
{
//Check if the given list is empty.
if(start == NULL)
printf("List Empty!!!");
else
{
printf("Current List:");
//Visit each node one by one
while(start != NULL)
{
printf(" %d", start->info);
start = start->next;
}
}
}
//Function to insert a node at end of single linked list with given data
node* insert_at_end(node *start, int data)
{
node *ptr;
//Create a new node and assign memory using malloc
node* new_node = (node*)malloc(sizeof(node));
if(new_node != NULL)
{
//Initialize new node with data.
new_node->info = data;
new_node->next = NULL;
}
else
{ //Panic
printf("\nMemory not allocated. Insertion failed!!!");
return start;
}
//If input list is empty. then new_node becomes the first node of link list.
if(start == NULL)
return new_node;
else
{
//travel to the last node of list
ptr = start;
while(ptr->next != NULL)
ptr = ptr->next;
//Attach the newly created node at end of list.
ptr->next = new_node;
return start;
}
}
//Delete a node from the end of a Single linked list
node* delete_at_end(node *start)
{
node *ptr;
//If input list is empty. nothing to delete just return.
if(start == NULL)
return NULL;
//Just one node in the given linked list.
else if(start->next == NULL)
{
//Free the memory assigned to the node.
free(start);
return NULL;
}
else
{ //Travel to the second last node of the linked list.
ptr = start;
while(ptr->next->next != NULL)
ptr = ptr->next;
//free the last node.
free(ptr->next);
ptr->next = NULL;
return start;
}
}
int main()
{
int i, data;
node *Head_node = NULL;
for(i = 1; i<=5 ; i++)
{
printf("\nEnter node %d :", i);
scanf("%d", &data);
// Insert at End
Head_node = insert_at_end(Head_node, data);
// Print current List
print_list(Head_node);
}
for(i = 5; i>=1 ; i--)
{
printf("\nDeleting node %d :\n", i);
// Delete at End
Head_node = delete_at_end(Head_node);
// Print current List
print_list(Head_node);
}
return 0;
}

Related

A function to join two linked lists

I implemented the list using
typedef int type;
typedef struct node{
int val;
struct node *next;
} node;
typedef struct list{
node *head;
}listtype ;
(The typedef type is just there so I can change the datatype of the list by just changing that one line)
And I implemented an insert function using
void insert(listtype *l, type elem, int pos) {
node *p = (node*)malloc(sizeof(node));
p->val = elem;
if (pos == 0) {
p->next = l->head;
l->head = p;
}
else {
node *q;
int i;
for (q = l->head, i = 0; i < pos - 1; i++) {
q = q->next;
}
p->next = q->next;
q->next = p;
}
}
The join function I came up with looks like this
void list_join(listtype *l,listtype *c) {
if (list_empty(*l)) {
printf("List empty");
}
else {
node *q;
int i = 0;
q = l->head;
while (q->next != NULL) {
insert(&c ,q->val, i + list_size(*c));
q = q->next;
i++;
}
}
}
But it doesn't work, or more accurately when I put it through my list view function it doesn't print out the correct contents (or anything at all really).
My function that prints the contents of the list:
void list_view(listtype l) {
if (list_empty(l))
printf("List empty");
else {
node *q;
q = l.head;
int i = 0;
while (q->next != NULL) {
printf("%d \t %d \n", i, q->val);
q = q->next;
i++;
}
printf("%d \t %d \n", i, list_tail(l));
}
}
I'm want to assume this function works properly and the issue is in the join function, but that's giving it a lot of credit.
The logic in the else block of the list_join function has a few issues:
The while condition should not test the ->next member, but the node itself, otherwise you'll not insert the tail value of the first list.
The insert function expects a listype* argument, not listype**, so you shouldn't pass &c, but c as first argument.
As in each iteration of the loop the c list will get longer, it is wrong to do i + list_size(*c), since both i and the size of the list will have increased. You'll want to calculate the length of the c list only once, and treat that as a constant that applies to all iterations equally. You could even initialise i with that list size instead of 0.
Here is a correction:
node *q;
int i = list_size(*c); // <-- get the list size only once
q = l->head;
while (q != NULL) { // No ->next
// Not &c; no list_size call
insert(c, q->val, i);
q = q->next;
i++;
}
This will fix the issue. But please note that there is lot of inefficiency in your approach which could be avoided if you would maintain a reference to the tail node of a list, and keep it updated. Then if you also create a separate method for appending a value at the end of a list (instead of providing a position), you can avoid some of the loops you currently have.

Used free() on each node but it's not emptying the list?

So first of all i have 2 linked lists one inside the other (like a matrix) and i made a function to delete an entire node. It seems to be freeing but when i print the value t it outputs weird characters.
Here are the structs used inside the list
typedef struct
{
char codigo[LEN_CODIGO + 1];
char partidaID[LEN_ID + 1];
char chegadaID[LEN_ID + 1];
Data datapartida;
Tempo horapartida;
Tempo duracao;
Data datachegada;
Tempo horachegada;
int capacidade;
int ocupacao;
} Voo;
typedef struct r
{
char *codReserva;
int nPassangeiros;
struct r *next;
} *ListaReservas;
typedef struct node
{
Voo voo;
ListaReservas nodeReservas; /*this is the head to a list inside this list*/
struct node *next;
} *Node;
in the following function i pretend to delete one node and all the nodes of nodeReservas in it, like deleting an entire column of a matrix.
Node eliminaNode(Node head, char codigo[])
{
Node n, prev;
ListaReservas r, temp;
for (n = head, prev = NULL; n != NULL; prev = n, n = n->next)
{
if (strcmp(n->voo.codigo, codigo) == 0) /*If it's the correct node*/
{
if (n == head)
head = n->next;
else
prev->next = n->next;
/*deletes nodeReservas*/
r = n->nodeReservas;
temp = r;
while(temp != NULL)
{
temp = temp->next;
free(r->codReserva);
free(r);
r= temp;
}
/*deletes the whole node*/
free(n);
}
}
return head;
}
I then use this code to tell me which reservations still exist in a node
for (r=n->nodeReservas; r != NULL; r= r->next)
printf("%s %d\n", r->codReserva, r->nPassangeiros);
For example after adding 3 reservations to lets say Node X and deleting the Node with the reservations with eliminaNode(headofList, X). After recreating the node with that same name 'X' and printing its reservations, instead of getting a empty line i get this:
-725147632
�+���U -725147632
#+���U -725147632
So what is the free() freeing? Is this happening because Lista reservas is a pointer?
free() returns the allocated block to the heap where it may be re-used for subsequent allocation requests. It does not (how could it?) modify the pointer to that block and if you retain such a pointer and re-use it after de-allocation, nothing good will happen.
What you should do is set the pointer to NULL (or a valid pointer such as that of the new next node) immediately after freeing the block so that you retain no reference to the now invalid block:
free(r->codReserva);
r->codReserva = NULL ;
free(r);
r= temp;
}
/*deletes the whole node*/
free(n);
n = NULL ;
Doing that should be a habit in C code. You could make things simpler by creating a function say:
void dealloc( void** ref )
{
free( *ref ) ;
*ref = NULL ;
}
Then instead of calling free( n ) you would call dealloc( &n ) for example.
There are other serious issues with this code. For example the code involving temp is somewhat over-complicated (and any code with a variable temp should raise alarm bells - you have given it scope over the entire function, and used it for more than one purpose - that is not good practice). Consider:
r = n->nodeReservas;
while( r != NULL)
{
ListaReservas new_next= r->next;
free(r->codReserva);
r->codReserva = NULL ;
free(r);
r = new_next;
}
There new_next is very localised (literally "temporary") and named appropriately so it is clear what it is. The next problem is that having assigned the value r you do nothing with it! It is presumably n->nodeReservas that you intended to update not r? Perhaps:
ListaReservas r = n->nodeReservas;
while( r != NULL)
{
ListaReservas new_next= r->next;
free(r->codReserva);
r->codReserva = NULL ;
free(r);
n->nodeReservas = new_next;
}
Note in each case the declaration of temporary variables at point of first use, to give the narrowest scope. Note that r is also temporary. However here it is not truly necessary - it is just a shorthand for n->nodeReservas - personally I'd eradicate it - if only to avoid exactly teh bug described above. Having multiple references to a single allocation is a recipe for bugs. Instead:
while( n->nodeReservas != NULL)
{
ListaReservas new_next = n->nodeReservas->next;
free(n->nodeReservas->codReserva);
n->nodeReservas->codReserva = NULL ;
free(n->nodeReservas);
n->nodeReservas = new_next;
}
I cannot say for sure there are not other bugs - that is just the part that had an obvious "code smell".

Doublind nodes linked list

I need to double every node in a linked list, the doubled nodes should be right after the copied node.
The function got the list.beg as parameter.
while (p != NULL)
{
q = p;
q->next= p->next;
p->next= q;
p = q->next;
}
For some reason the loop never ends.
You are just doing q = p;, so you're creating a node that links back to itself. That is, what you're doing is equivalent to:
p->next = p;
You have to allocate a new node for q.
It's a little difficult to be sure without the struct definition, but, here's some refactored code:
while (p != NULL) {
q = malloc(sizeof(*q));
// NOTE: this only works if the node does _not_ have a pointer to some data
// otherwise, a "deep copy" is required
*q = *p;
p->next = q;
p = q->next;
printf("%i\n", q->data.number);
}
When I mentioned "deep copy" above, it would apply if the node had an element such as:
const char *str;
Where str was created initially via (e.g.):
p->str = strdup(buf);
possibly as part of an fgets(buf,sizeof(buf),stdin); loop.
Then, in the duplication code, after the *q = *p;, we'd need:
q->str = strdup(p->str);

C programing: How to pop last element on linked list?

I am beginner programer and one week ago i was introduced to linked list however I'm still struggling to wrap my head around this.
Currently trying to write a function that will help me remove last element from the linked list. I would appreciate some explanation what am i doing wrong here. Thank You for any suggestions.
I'm not allowed to touch or modify current structs
Here is my structs:
typedef struct node {
ElemType val;
struct node *next;
} NODE;
struct list_struct {
NODE *front;
NODE *back;
};
And heres my current code:
if list is empty, we do nothing and return arbitrary value
otherwise, the last element in the list is removed and its
value is returned.
ElemType lst_pop_back(LIST *l) {
NODE * p = l->front;
NODE * trail = l->front;
if( p == NULL) return 0;
if( lst_len(l) == 1){
free(p);
l->front = NULL;
l->back = NULL;
}
else{
p=p->next;
while( p != NULL){
if( p->next == NULL) free(p);
trail = trail->next;
p=p->next;
}
trail= trail->next;
trail->next= NULL;
}
return 0;
}
I'm using Xcode on MAC and the error that i get is: Thread 1: EXC_ACCESS(code=1, address=0x8)
The XCode error EXC_BAD_ACCESS(code=1, address=0x8) means that somebody tries to access unaccessable memory. XCode's boundary checks are said to be good, so let's trust them. It is a bit sad that the OP doesn't tell us the exact line-number but one can guess. I would concur with Katerina B. here and assume the same lines as the culprit.
In detail:
ElemType lst_pop_back(LIST * l)
{
// p and trail point to the first node
NODE *p = l->front;
NODE *trail = l->front;
if (p == NULL)
return 0;
if (lst_len(l) == 1) {
free(p);
l->front = NULL;
l->back = NULL;
} else {
p = p->next;
// Now: trail->next points to the old p
// and p to p->next, that is: trail points
// to the node before p
// while trail is not the last node
while (p != NULL) {
// if p is the last node
if (p->next == NULL){
// release memory of p, p points to nowhere from now on
free(p);
}
// Following comments assume that p got free()'d at this point
// because trail points to the node before p
// trail->next points to the node p pointed to
// before but p got just free()'d
trail = trail->next;
// p got free()'d so p->next is not defined
p = p->next;
}
// assuming p got free()'d than trail->next is one step
// further into unknown, pardon, undefined territory
trail = trail->next;
trail->next = NULL;
}
return 0;
}
I think the error you're getting happens when you try to access something that's already been deallocated. You're doing that here:
if( p->next == NULL) free(p);
trail = trail->next;
p=p->next;
Since the list structure contains a back pointer, I'd recommend using that to help you. Maybe let p move through the list until p->next points to the same thing as the list's back pointer, then make p->next null.
Also, should the function POP or REMOVE from the list? Your question says removing but the function is named lst_pop_back. If you're poping, you'll need to return the last value too.

Single linked list in C

I am trying to write a singly-linked list in C. So far, I just get segmentation faults.
I am probably setting the pointers wrong, but I just couldn't figure how to do it correctly.
The list should be used for "processors" sorted from highest priority (at the beginning of the list) to lowest priority (at the end of the list). Head should point to the first element, but somehow I am doing it wrong.
First of all here is the code:
struct process {
int id;
int priority;
struct process *next;
}
struct process *head = NULL;
void insert(int id, int priority) {
struct process * element = (struct process *) malloc(sizeof(struct process));
element->id = id;
element->priority = priority;
while(head->next->priority >= priority)
head = head->next;
element->next = head->next;
head->next = element;
// I put here a printf to result, which leads to segmenatition fault
// printf("%d %d\n", element->id, element->priority);
}
/* This function should return and remove element with the highest priority */
int pop() {
struct process * element = head->next;
if(element == NULL)
return -1;
head->next = element->next;
free(element);
return element->id;
}
/* This function should remove a element with a given id */
void popId(int id) {
struct process *ptr = head;
struct process *tmp = NULL;
while(prt != NULL) {
if(ptr->id == id) {
ptr->next = ptr->next->next;
tmp = ptr->next;
} else {
prt = ptr->next;
}
}
free(tmp);
}
Unfortunately, I could not try out pop() and popId() due to the segmentation fault.
May anyone tell me what I am doing wrong?
EDIT: Now, I edited the insert function. It looks like this:
void insert(int id, int priority) {
struct process * element = (struct process *) malloc(sizeof(struct process));
struct process * temp = head;
element->id = id;
element->priority = priority;
if(head == NULL) {
head = element; // edited due to Dukeling
element->next = NULL;
} else {
while(temp->next != NULL && temp->next->priority >= priority)
temp = temp->next;
element->next = head->next;
head->next = element;
}
// I put here a printf to result, which leads to segmenatition fault
// printf("%d %d\n", element->id, element->priority);
}
But I still get segmentation fault for pop() and popId(). What did I miss here?
You don't check if head is NULL in insert.
You actually don't check if head is NULL in any function. You should, unless you want to put some dummy element on head, to simplify the code.
For insert:
About these lines:
while(head->next->priority >= priority)
head = head->next;
If head is NULL, that's not going to work. This may not actually be a problem if head can never be NULL for whichever reason (e.g. it has a dummy element as gruszczy mentioned).
You're changing head, thus you're getting rid of the first few elements every time you insert. You probably need a temp variable.
You need to also have a NULL check in case you reach the end of the list.
So, we get:
struct process *temp = head;
while (temp->next != NULL && temp->next->priority >= priority)
temp = temp->next;
For pop:
If the first element isn't a dummy element, then you should be returning the ID of head, not head->next (and you were trying to return a value of an already freed variable - this is undefined behaviour).
if (head == NULL)
return -1;
int id = head->id;
struct process *temp = head;
head = head->next;
free(temp);
return id;
For popId:
You're checking ptr's ID, but, if it's the one we're looking for, you're removing the next element rather than ptr. You should be checking the next one's ID.
head == NULL would again need to be a special case.
The free should be in the if-statement. If it isn't, you need to cater for it not being found or finding multiple elements with the same ID.
You should break out of the loop in the if-statement if there can only be one element with that ID, or you want to only remove the first such element.
I'll leave it to you to fix, but here's a version using double-pointers.
void popId(int id)
{
struct process **ptr = &head;
while (*ptr != NULL)
{
if ((*ptr)->id == id)
{
struct process *temp = *ptr;
*ptr = (*ptr)->next;
free(temp);
}
else
{
prt = &(*ptr)->next;
}
}
}
Note that the above code doesn't break out of the loop in the if-statement. This can be added if you're guaranteed to only have one element with some given ID in the list, or you want to just delete the first such element.
Your not checking your pointers before accessing their values for dereference. This will automatically lead to undefined behavior if the pointer is invalid (NULL or indeterminate). With each implementation below, note we don't access data via dereference unless the pointer is first-known as valid:
Implementation: insert()
void insert(int id, int priority)
{
struct process **pp = &head;
struct process *element = malloc(sizeof(*element);
element->id = id;
element->priority = priority;
while (*pp && (*pp)->priority >= priority)
pp = &(*pp)->next;
element->next = *pp;
*pp = element;
}
Implementation: pop()
Your pop() function appears to be designed to return the popped value. While this isn't entirely uncommon it has the undesirable side-effect of having no mechanism for communicating to the caller that the queue is empty without a sentinel-value of some sort (such as (-1) in your case. This is the primary reason most queues have a top(), pop(), and isempty() functional interface. Regardless, assuming (-1) is acceptable as an error condition:
int pop()
{
struct process *tmp = head;
int res = -1;
if (head)
{
head = head->next;
res = tmp->id;
free(tmp);
}
return res;
}
Implementation: popId()
Once again, looking for a specific node can be accomplished with a pointer-to-pointer in a fairly succinct algorithm, with automatic updating done for you due to using the actual physical pointers rather than just their values:
void popId(int id)
{
struct process ** pp = &head, *tmp = NULL;
while (*pp && (*pp)->id != id)
pp = &(*pp)->next;
if (*pp)
{
tmp = *pp;
*pp = tmp->next;
free(tmp);
}
}
I strongly advise stepping through each of these with a debugger to see how they work, particularly the insert() method, which has quite a lot going on under the covers for what is seemingly a small amount of code.
Best of luck

Resources