見出し画像

(答案再提出)C言語教室 第21回 - 循環リスト(アフターパーリィ編)

kznさん、いつも丁寧な添削ご指導、有り難うございます!

今回も一発合格とはなりませんでした(涙
恒例となりつつある答案再提出です(苦笑

しまった、日付が変わった。もう寝ます。


(再提出)演習1. リストに引き数で渡した値を持つノードのアドレスを返す関数

//exercise21-1. リストに引き数で渡した値を持つノードのアドレスを返す関数
Node * search_node(List *list_head, int val) {
  Node *current_node = list_head->top->next;

  while (current_node != list_head->top) {
    if (current_node->val == val) {
      return current_node;
    }
    current_node = current_node->next;
  }
  return NULL;
}

当初のバージョンでは、関数で単にアドレスを返してそれを表示してもなんの意味もない16進数だし、折角だから最初から何番目なのかを返そうなんて遊んでみたら、見事にご指導をいただきました。

素直にアドレスを返して表示するように修正しました。
番兵nodeの次(list_head->top->next)から、番兵nodeに至るまでの間、指定された値を持つnodeを探します。見つからなければ、NULLを返します。

この関数を呼ぶ側でNULLをチェックして制御します。


(再提出)演習2. リストに含まれるノードへのポインタと値を引き数とし、渡したノードの位置に渡した値のノードを挿入する関数

//exercise21-2. リストに含まれるノードへのポインタと値を引き数とし、渡したノードの位置に渡した値のノードを挿入する関数
void insert_node(Node *pointer, int val) {
  Node *node_new = create_node(val);
  Node *node_next = pointer->next;

  pointer->next = node_new;
  node_new->prev = pointer;
  node_new->next = node_next; 
  node_next->prev = node_new;
}

上記search_node()関数の仕様変更に伴って修正しました。
指定されたnodeが存在するかどうかは、この関数insert_node()を呼ぶ前にチェックしています。


(再提出)演習3. リストに含まれるノードへのポインタを渡して、そのノードをリストから削除する関数


//exercise21-3. リストに含まれるノードへのポインタを渡して、そのノードをリストから削除する関数
void delete_specified_node(Node *pointer) {
  Node *node_del = pointer;
  Node *node_next = node_del->next;
  Node *node_prev = node_del->prev;
  node_prev->next = node_next;
  node_next->prev = node_prev;
  printf("specified node with value %d was deleted.\n", node_del->val);
  free(node_del);
}

上記search_node()関数の仕様変更に伴って修正しました。
当初、削除しようとするnodeが番兵でないかをチェックしていましたが、削除しようとするnodeを検索するsearch_node()関数は戻り値として番兵を指定しないのでこのチェックを外しました。


(再提出)課題3. リストの先頭の要素を削除する。

(再提出)課題4. リストの最後の要素を削除する。

//lesson21-3. リストの先頭の要素を削除する。
void delete_first_node(List * list_head) {
  Node *node_del = list_head->top->next;
  if (node_del == list_head->top) {
    printf("*** warning: nothing to delete.\n");
  } else {
    Node *node_next = node_del->next;
    list_head->top->next = node_next;
    node_next->prev = list_head->top;
    printf("fisrt node with value %d was deleted.\n", node_del->val);
    free(node_del);
  }
}

//lesson21-4. リストの最後の要素を削除する。
void delete_last_node(List * list_head) {
  Node *node_del = list_head->top->prev;
  if (node_del == list_head->top) {
    printf("*** warning: nothing to delete.\n");
  } else {
    Node *node_prev = node_del->prev;
    list_head->top->prev = node_prev;
    node_prev->next = list_head->top;
    printf("last node with value %d was deleted.\n", node_del->val);
    free(node_del);
  }
}

処理結果がエラーとなる可能性のあるものについては、関数を呼ぶ側で制御できるようにした方が良いと思ったのですが、削除処理結果を関数の戻り値で返してもたいして活用できていなかったので、関数をvoid型として単純化しました。


実行結果


jm3nrhMac-mini-:c akio$ gcc lesson21_r1.0.c
jm3nrhMac-mini-:c akio$ ./a.out
空の状態のlist
list: 

Lesson21-1: リストの先頭に要素を追加する(20, 10)
new node with value 20 was prepended.
new node with value 10 was prepended.
list: 10 20 

Lesson21-2: リストの最後に要素を追加する(30, 40)
new node with value 30 was appended.
new node with value 40 was appended.
list: 10 20 30 40 

Lesson21-3: リストの先頭の要素を削除する(要素数よりも一回多く実行する)
fisrt node with value 10 was deleted.
fisrt node with value 20 was deleted.
fisrt node with value 30 was deleted.
fisrt node with value 40 was deleted.
*** warning: nothing to delete.
list: 

Lesson21-4: リストの最後の要素を削除する(要素数よりも一回多く実行する)
new node with value 60 was appended.
new node with value 70 was appended.
new node with value 80 was appended.
list: 60 70 80 

last node with value 80 was deleted.
last node with value 70 was deleted.
last node with value 60 was deleted.
*** warning: nothing to delete.
list: 

exercise21-1: リストに引き数で渡した値(30)を持つノードのアドレスを返す関数
new node with value 10 was appended.
new node with value 30 was appended.
new node with value 50 was appended.
list: 10 30 50 

specified node with value 30 was found at 0x6000035280a0
list: 10 30 50 

exercise21-2: 値(30)を持つノードへのポインタと値(40)を引き数とし、
    指定したnodeの次の位置に、値(40)をもつ新しいノードを挿入する関数
list: 10 30 40 50 

exercise21-3: リストに含まれるノードへのポインタを渡して、そのノードをリストから削除する関数
specified node with value 30 was deleted.
list: 10 40 50 

*** warning: specified node with value 60 was not found.
list: 10 40 50 

jm3nrhMac-mini-:c akio$ 

ソースコード

/**********************************************************************
	lesson21 circularly-linked list
		by Akio van der Meer

[変更履歴]
 (無印)  記事初投稿時のコード
 r1.0   ・search_node()の戻り値を、検出したnodeのアドレスに変更し、
          関連するinsert_node()とdelete_specified_node()を修正。
        ・delete_first_node(), delete_last_node(),
	         delete_specified_node()をvoid型に修正。

**********************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define SRCH 30
#define INSRT 40
#define DLT1 30
#define DLT2 60

typedef struct node {
  int val;
  struct node *next;
  struct node *prev;
} Node;

typedef struct list {
  struct node *top;
} List;

//prepared functions by kzn -------------------------------------------
Node *create_node(int val) {
  Node *node = (Node*)malloc(sizeof(Node));
  node->val = val;
  node->next = NULL;
  node->prev = NULL;
  return node;
}

void create_list(List *list) {
  Node *dummy_node = (Node *)malloc(sizeof(Node));
  dummy_node->next = dummy_node;
  dummy_node->prev = dummy_node;
  list->top =  dummy_node;
}

void destroy_list(List *list) {
   Node *current_node = list->top->next;
   Node *next_node;
   while (current_node != list->top) {
        next_node = current_node->next;
        free(current_node);
        current_node = next_node;
   }
   free(list->top);
   list->top = NULL;
}

void print_list(List *list) {
  Node *current_node = list->top->next;
  printf("list: ");
  while (current_node != list->top) {
    printf("%d ", current_node->val);
    current_node = current_node->next;
  }
  printf("\n\n");
}

//functions -----------------------------------------------------------
//lesson21-1. リストの先頭に要素を追加する。
void prepend_node(List * list_head, int val) {
  Node *node_new = create_node(val);
  Node *node_next = list_head->top->next;

  list_head->top->next = node_new;
  node_new->prev = list_head->top;
  node_new->next = node_next; 
  node_next->prev = node_new;

  printf("new node with value %d was prepended.\n", val);
}
 
//lesson21-2. リストの最後に要素を追加する。
void append_node(List * list_head, int val) {
  Node *node_new = create_node(val);
  Node *node_prev = list_head->top->prev;

  list_head->top->prev = node_new;
  node_new->next = list_head->top;
  node_new->prev = node_prev; 
  node_prev->next = node_new;

  printf("new node with value %d was appended.\n", val);
}

//lesson21-3. リストの先頭の要素を削除する。
void delete_first_node(List * list_head) {
  Node *node_del = list_head->top->next;
  if (node_del == list_head->top) {
    printf("*** warning: nothing to delete.\n");
  } else {
    Node *node_next = node_del->next;
    list_head->top->next = node_next;
    node_next->prev = list_head->top;
    printf("fisrt node with value %d was deleted.\n", node_del->val);
    free(node_del);
  }
}

//lesson21-4. リストの最後の要素を削除する。
void delete_last_node(List * list_head) {
  Node *node_del = list_head->top->prev;
  if (node_del == list_head->top) {
    printf("*** warning: nothing to delete.\n");
  } else {
    Node *node_prev = node_del->prev;
    list_head->top->prev = node_prev;
    node_prev->next = list_head->top;
    printf("last node with value %d was deleted.\n", node_del->val);
    free(node_del);
  }
}

//exercise21-1. リストに引き数で渡した値を持つノードのアドレスを返す関数
Node * search_node(List *list_head, int val) {
  Node *current_node = list_head->top->next;

  while (current_node != list_head->top) {
    if (current_node->val == val) {
      return current_node;
    }
    current_node = current_node->next;
  }
  return NULL;
}

//exercise21-2. リストに含まれるノードへのポインタと値を引き数とし、渡したノードの位置に渡した値のノードを挿入する関数
void insert_node(Node *pointer, int val) {
  Node *node_new = create_node(val);
  Node *node_next = pointer->next;

  pointer->next = node_new;
  node_new->prev = pointer;
  node_new->next = node_next; 
  node_next->prev = node_new;
}
 
//exercise21-3. リストに含まれるノードへのポインタを渡して、そのノードをリストから削除する関数
void delete_specified_node(Node *pointer) {
  Node *node_del = pointer;
  Node *node_next = node_del->next;
  Node *node_prev = node_del->prev;
  node_prev->next = node_next;
  node_next->prev = node_prev;
  printf("specified node with value %d was deleted.\n", node_del->val);
  free(node_del);
}

 int main() {
  List list_head;
  create_list(&list_head); 

  printf("空の状態のlist\n");
  print_list(&list_head);

  //lesson21-1. リストの先頭に要素を追加する。
  printf("Lesson21-1: リストの先頭に要素を追加する(20, 10)\n");
  prepend_node(&list_head, 20);
  prepend_node(&list_head, 10);
  print_list(&list_head);

  //lesson21-2. リストの最後に要素を追加する。
  printf("Lesson21-2: リストの最後に要素を追加する(30, 40)\n");
  append_node(&list_head, 30);
  append_node(&list_head, 40); 
  print_list(&list_head);
  
  //lesson21-3. リストの先頭の要素を削除する。
  printf("Lesson21-3: リストの先頭の要素を削除する(要素数よりも一回多く実行する)\n");

  delete_first_node(&list_head);
  delete_first_node(&list_head);
  delete_first_node(&list_head);
  delete_first_node(&list_head);
  delete_first_node(&list_head);
  print_list(&list_head);

  //lesson21-4. リストの最後の要素を削除する。
  printf("Lesson21-4: リストの最後の要素を削除する(要素数よりも一回多く実行する)\n");
  append_node(&list_head, 60);
  append_node(&list_head, 70); 
  append_node(&list_head, 80); 
  print_list(&list_head);

  delete_last_node(&list_head);
  delete_last_node(&list_head);
  delete_last_node(&list_head);
  delete_last_node(&list_head);
  print_list(&list_head);

  //exercise21-1. リストに引き数で渡した値を持つノードのアドレスを返す関数
  printf("exercise21-1: リストに引き数で渡した値(%d)を持つノードのアドレスを返す関数\n", SRCH);
  append_node(&list_head, 10);
  append_node(&list_head, 30); 
  append_node(&list_head, 50); 
  print_list(&list_head);

  Node *search_result = NULL;
  search_result = search_node(&list_head, SRCH);
  if (search_result != NULL) {
    printf("specified node with value %d was found at %p\n", SRCH, search_result);    
  } else {
    printf("*** warning: specified node with value %d was not found.\n", SRCH);  
  }
  print_list(&list_head);

  //exercise21-2. リストに含まれるノードへのポインタと値を引き数とし、渡したノードの位置に渡した値のノードを挿入する関数
  printf("exercise21-2: 値(%d)を持つノードへのポインタと値(%d)を引き数とし、\n", SRCH, INSRT);
  printf("    指定したnodeの次の位置に、値(%d)をもつ新しいノードを挿入する関数\n", INSRT);

  search_result = search_node(&list_head, SRCH);
  if (search_result != NULL) {
    insert_node(search_result, INSRT);
  } else {
    printf("*** warning: specified node with value %d was not found.", SRCH);  
  }
  print_list(&list_head);

  //exercise21-3. リストに含まれるノードへのポインタを渡して、そのノードをリストから削除する関数
  printf("exercise21-3: リストに含まれるノードへのポインタを渡して、そのノードをリストから削除する関数\n");

  search_result = search_node(&list_head, DLT1);
  if (search_result != NULL) {
    delete_specified_node(search_result);
  } else {
    printf("*** warning: specified node with value %d was not found.\n", DLT1);  
  }
  print_list(&list_head);

  search_result = search_node(&list_head, DLT2);
  if (search_result != NULL) {
    delete_specified_node(search_result);
  } else {
    printf("*** warning: specified node with value %d was not found.\n", DLT2);  
  }
  print_list(&list_head);

  //終了処理
  destroy_list(&list_head);

  return 0;
 }

後記

関数のロジックの修正はすぐ済んだけど、相変わらずポインタの設定で手間取る。とりあえず動くようになったけど、これが正しい設定になっているのかは未だに自信がない。やれやれ。


ここまで読んでいただき有り難うございました。





いいなと思ったら応援しよう!

Akio van der Meer
これまでの収益は全て、それを必要としておられる方々へ、支援機関を通して寄付させていただきました。この活動は今後も継続したいと思っています。引き続きよろしくお願いいたします。