Tuesday, October 24, 2006

Open file in directory (ต่อ)

ใน blog ที่แล้วผมเขียนให้ เปิด file ใน directory โดยถ้า file ข้างในนั้นไม่เป็น directory แต่ถ้าต้องการให้ traverse ลึกเข้าไปใน directory ข้างในด้วยอาจจะต้องอาศัย recursive มาช่วย เขียนออกมาได้เป็นดังนี้


#include <stdio.h>
#include <fcntl.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>

void listfileindir(const char *path);

int main(int argc, char *argv[])
{
if(argc != 2)
{
fprintf(stderr, "%s [dir]\n", argv[0]);
return -1;
}

listfileindir(argv[1]);

return 0;
}

void listfileindir(const char *path)
{
DIR *dir;
struct dirent *dp;
struct stat st;
FILE *fp;
char filename[100] = {0};
dir = opendir(path);

if(dir != NULL)
{
while((dp = readdir(dir)) != NULL)
{
if(!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
{
continue;
}
sprintf(filename, "%s/%s", path, dp->d_name);
stat(filename, &st);
if(S_ISREG(st.st_mode))
{
if((fp = fopen(filename, "r")) != NULL)
{
printf("%s\n", filename);
fclose(fp);
}
}
else if(S_ISDIR(st.st_mode))
{
listfileindir(filename);
}
else
{
continue;
}
}
closedir(dir);
}
}

เปิด File ใน Directory

เคยเขียนตอบในกระทู้เรื่องการเข้าไป เปิด file ในแต่ละ folder (directory) เลยเอามาลงทิ้งไว้ ใช้ได้บน Solaris, Linux ...


#include <stdio.h>
#include <dirent.h>
#include <sys/types.h>

int main(int argc, char *argv[])
{
DIR *dir;
struct dirent *dp;
FILE *fp;
char filename[100] = {0};

if(argc != 2)
{
fprintf(stderr, "%s [dir]\n", argv[0]);
return -1;
}

dir = opendir(argv[1]);

if(dir != NULL)
{
while((dp = readdir(dir)) != NULL)
{
sprintf(filename, "%s/%s", argv[1], dp->d_name);
if((fp = fopen(filename, "r")) != NULL)
{
printf("%s\n", dp->d_name);
fclose(fp);
}
}
}

closedir(dir);

return 0;
}

Data Structure in C

Code นี้เขียนเอาไว้นานมากแล้วตั้งแต่สมัยเรียน สมัย C++ ยังไม่เริ่มโด่งดัง

ขั้นแรกต้อง คิดก่อนว่า อะไรจะเป็นข้อมูลใน list คิดแบบ abstract ก็คือ node นั่นเอง ก็ทำการ กำหนดขึ้นมาว่า node มันเป็นอย่างไร สามารถทำอะไรมันได้บ้าง โดยสรุปก็คือ node จะประกอบไปด้วยส่วนที่เป็น data และส่วนที่เป็นตัวชี้ไปยัง node อื่น ข้างหน้า และ ข้างหลัง (เพื่อง่ายในการเขียนรูปแบบอื่นต่อไป) และ เืพื่อให้ยืดหยุ่น จึงกำหนด data เป็น (void *)

ก็สรุปออกมาเป็น file node.h

/* node.h */
#ifndef _NODE_H_
#define _NODE_H_
typedef struct node
{
struct node* next;
struct node* prev;
void *data;
} node_t;

node_t* node_create(void *data, int dtsize);

inline int node_delete(node_t *n);
#endif

แล้วก็เขียนโค้ดมันใน file node.c
เมื่อ create node ขึ้นมา ตัว node จะต้องกำหนดค่าที่จะส่งเข้ามา และก็หนด prev และ next ให้เป็น NULL ไปก่อน
เวลา delete node ก็ต้องทำการ free data ก่อนแล้วค่อย ทำลายตัวเองทิ้ง

/* node.c */
#include
#include
#include "node.h"

inline node_t* node_create(void *data, int dtsize)
{
node_t *n;
n = (node_t *)malloc(sizeof(node_t));
n->next = NULL;
n->prev = NULL;
n->data = (void *)malloc(dtsize);
memcpy(n->data, data, dtsize);
return n;
}

inline int node_delete(node_t *n)
{
free(n->data);
free(n);
return 0;
}


เมื่อได้ node แล้ว ต่อไปก็คิดว่า list เราจะมีหน้าตาอย่างไร ในที่นี้จะให้ list เป็นตัว เก็บ node เริ่มต้น และ node ท้ายสุด และมีการเก็บขนาดไว้
ส่วน operation ของ list ก็มีเยอะ (ดูใน code) เช่น add หน้า add หลัง pop หน้า pop หลัง add ที่ตำแหน่งใด ๆ remove ที่ตำแหน่งใด ๆ ฯลฯ


/* list.h */
#ifndef _LIST_H_
#define _LIST_H_

#include "node.h"

typedef struct list
{
struct node* head;
struct node* tail;
int length;
} list_t;

list_t* list_init();

inline int list_add_tail(list_t *l, node_t *n);
inline int list_add_head(list_t *l, node_t *n);

inline node_t* list_pop_tail(list_t *l);
inline node_t* list_pop_head(list_t *l);

inline int list_remove_tail(list_t *l);
inline int list_remove_head(list_t *l);

inline int list_add_nth(list_t *l, int index, node_t *n);
inline node_t* list_pop_nth(list_t *l, int index);
inline int list_remove_nth(list_t *l, int index);

inline int list_deinit(list_t *l);

inline void list_print(list_t *l);
#endif


ส่วนตัวโค้ดก็ตามนี้ อธิบายไม่หมด คงต้องไปศึกษาเพิ่มเติมเอง


/* list.c */
#include
#include
#include "list.h"

inline list_t* list_init()
{
list_t *l;
l = (list_t *)malloc(sizeof(list_t));
l->head = NULL;
l->tail = NULL;
l->length = 0;
return l;
}

inline int list_add_tail(list_t *l, node_t *n)
{
if(l->tail != NULL)
{
n->prev = l->tail;
l->tail->next = n;
l->tail = n;
}
else
{
l->head = n;
l->tail = n;
}
l->length++;
return 0;
}

inline int list_add_head(list_t *l, node_t *n)
{
if(l->head != NULL)
{
n->next = l->head;
l->head->prev = n;
l->head = n;
}
else
{
l->head = n;
l->tail = n;
}
l->length++;
return 0;
}



inline int list_deinit(list_t *l)
{
node_t* buff;
while(l->head != NULL && l->tail != NULL)
{
list_remove_tail(l);
}
free(l);
return 0;
}

inline node_t* list_pop_tail(list_t *l)
{
node_t *buff;
buff = l->tail;
l->tail = buff->prev;
if(l->tail != NULL) l->tail->next = NULL;
buff->next = NULL;
buff->prev = NULL;
l->length--;
return buff;
}

inline node_t* list_pop_head(list_t *l)
{
node_t *buff;
buff = l->head;
l->head = buff->next;
if(l->head != NULL) l->head->prev = NULL;
buff->next = NULL;
buff->prev = NULL;
l->length--;
return buff;
}

inline int list_remove_tail(list_t *l)
{
node_t *buff;
buff = list_pop_tail(l);
node_delete(buff);
return 0;
}

inline int list_remove_head(list_t *l)
{
node_t *buff;
buff = list_pop_head(l);
node_delete(buff);
return 0;
}

inline void list_print(list_t *l)
{
node_t *n = l->head;
printf("current length = %d: ", l->length);
while(n != NULL)
{
printf("%s ", (char *)(n->data));
n = n->next;
}
printf("\n");
}

inline int list_add_nth(list_t *l, int index, node_t *n)
{
node_t *t;
int i;
if(index > l->length || index < index ="=" index ="=">length )
{
list_add_tail(l, n);
}
else
{
for(t = l->head, i = 0; i != index; i++)
{
t = t->next;
}
t->prev->next = n;
n->next = t;
n->prev = t->prev;
t->prev = n;
l->length++;
}
return 0;
}

inline node_t* list_pop_nth(list_t *l, int index)
{
node_t *t;
node_t *n;
int i;
if(index >= l->length || index < index ="=" t =" list_pop_head(l);" index ="=">length - 1)
{
t = list_pop_tail(l);
}
else
{
for(t = l->head, i = 0; i != index; i++)
{
t = t->next;
}
t->prev->next = t->next;
t->next->prev = t->prev;
t->next = NULL;
t->prev = NULL;
l->length--;
}
return t;
}

inline int list_remove_nth(list_t *l, int index)
{
node_t *n = list_pop_nth(l, index);
if(n != NULL) node_delete(n);
return (n == NULL)?-1:0;
}


จากการ design ในลักษณะนี้จะเห็นได้ว่า เราสามารถเพิ่ม stack กะ queue support ได้ไม่ยากเลย เพราะ list มี operation พร้อมหมดแล้ว สำหรับ ทั้ง?stack และ queue

ดังนี้


/* stack.h */
#include "node.h"
#include "list.h"

typedef list_t stack_t;

inline stack_t* stack_init();

inline int stack_push(stack_t *s, node_t *);
inline node_t* stack_pop(stack_t *s);

inline void stack_print(stack_t *s);

inline int stack_deinit(stack_t *s);

/* stack.c */
#include
#include "stack.h"

inline stack_t* stack_init()
{
list_t* l;
l = list_init();
return l;
}

inline int stack_push(stack_t *s, node_t *n)
{
list_add_head(s, n);
return 0;
}

inline node_t* stack_pop(stack_t *s)
{
node_t *n;
n = list_pop_head(s);
return n;
}

inline void stack_print(stack_t *s)
{
list_print(s);
}

inline int stack_deinit(stack_t *s)
{
list_deinit(s);
return 0;
}

/* queue.h */
#include "node.h"
#include "list.h"

typedef list_t queue_t;

inline queue_t* queue_init();
inline int queue_push(queue_t *q, node_t *n);
inline node_t* queue_pop(queue_t *q);

inline void queue_print(queue_t* q);

inline int queue_deinit(queue_t* q);

/* queue.c */
#include
#include "queue.h"

inline queue_t* queue_init()
{
list_t *l;
l = list_init();
return l;
}

inline int queue_push(queue_t *q, node_t *n)
{
list_add_head(q, n);
return 0;
}

inline node_t* queue_pop(queue_t *q)
{
node_t *n;
n = list_pop_tail(q);
return n;
}

inline void queue_print(queue_t* q)
{
list_print(q);
}

inline int queue_deinit(queue_t* q)
{
list_deinit(q);
return 0;
}


จะเห็นได้ว่าแทบไม่ต้องเขียนอะไรเลย แค่ reuse list operation เท่านั้น
ส่วนการเรียกใช้ก็

#include
#include "node.h"
#include "list.h"
#include "stack.h"
#include "queue.h"

int main(int argc, char *argv[])
{
list_t *l;
stack_t *s;
queue_t *q;
node_t *n;
int i;

l = list_init();
printf("\n========== Start List =========\n");
for(i = 1; i < n =" node_create((void">length >= 3)
{
n = node_create((void *)"HELLO", sizeof("HELLO") + 1);
list_print(l);
printf("\tAdding: %s at %d\n", (char *)(n->data), 3);
list_add_nth(l, 3, n);

list_print(l);
n = list_pop_nth(l, 3);
printf("\tPopping at %d: %s\n", 3, (char *)(n->data));
list_print(l);
}
printf("\n========== End List =========\n");
list_deinit(l);

q = queue_init();
printf("\n========== Start Queue =========\n");
for(i = 1; i < n =" node_create((void" i =" 1;" n =" queue_pop(q);">data));
}
printf("\n========== End Queue =========\n");
queue_deinit(q);

s = stack_init();
printf("\n========== Start Stack =========\n");
for(i = 1; i < n =" node_create((void" i =" 1;" n =" stack_pop(s);">data));
}
printf("\n========== End Stack =========\n");
stack_deinit(s);

return 0;
}


ผลลัพธ์ที่ได้ เป็นดังนี้

khunpid@PETER2003:/home/khunpid/container> mainapp 1 2 3

========== Start List =========
Pushing: 1
current length = 1: 1
Pushing: 2
current length = 2: 1 2
Pushing: 3
current length = 3: 1 2 3

current length = 3: 1 2 3
Adding: HELLO at 3
current length = 4: 1 2 3 HELLO
Popping at 3: HELLO
current length = 3: 1 2 3

========== End List =========

========== Start Queue =========
Pushing: 1
current length = 1: 1
Pushing: 2
current length = 2: 2 1
Pushing: 3
current length = 3: 3 2 1

current length = 3: 3 2 1
Popping: 1
current length = 2: 3 2
Popping: 2
current length = 1: 3
Popping: 3

========== End Queue =========

========== Start Stack =========
Pushing: 1
current length = 1: 1
Pushing: 2
current length = 2: 2 1
Pushing: 3
current length = 3: 3 2 1

current length = 3: 3 2 1
Popping: 3
current length = 2: 2 1
Popping: 2
current length = 1: 1
Popping: 1

========== End Stack =========

Sunday, December 25, 2005

Sieve of Eratosthene

Eratosthenes เป็นนักคณิตศาสตร์ชาวกรีก ที่คิดค้นสิ่งต่าง ๆ ไว้มากพอสมควร แต่ วันนี้เราจะมาพูดกันถึง การหาค่า prime หรือจำนวนเฉพาะ ที่ใช้วิธีตัดออกทีละชุด (screen หรือ sieve นั่นเอง)
วิธีการของเค้าก็ง่าย ๆ ครับ คือ ในตัวเลขจำกัดจำนวนนึงเราสามารถหาค่า จำนวนเฉพาะได้โดยใช้ ตัวเลขเริ่มต้น (2) เพื่อที่จะนำไป หาค่าที่เป็นตัวคูณเพื่อ screen ตัวที่เป็น ค่าที่เกิดจากการคูณออกจากความน่าจะเป็นที่จะเป็น จำนวนเฉพาะ โดยตัวที่จะนำมาสกรีน ก็นำมาจถึงแค่ ตัวเลขที่ น้อยกว่าหรือเท่ากับ sqrt ของ ค่าสูงสุดที่ต้องการหา (ตรงนี้พิสูจน์ไม่ยาก)
เช่น หาจำนวนเฉพาะ จนถึง 20
2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20
ใช้ 2 screen (2^2 = 4 < 20)
2, 3, 5, 7, 9, 11, 13, 15, 17, 19
ใช้ 3 screen (3^2 = 9 < 20)
2, 3, 5, 7, 11, 13, 17, 19
5 ไม่ต้องนำมาสกรีนอีก เพราะ 5^2 > 20
เพราะฉะนั้นค่าที่ไม่ถูกตัดทิ้งทั้งหมดก็คือ เลขจำนวนเฉพาะ
เขียนด้วย C++ ได้ดังนี้


vector<bool> GetPrimeBitSets(int from, int to)
{
vector<bool> bs (to + 1); //Create a bool vector to hold a primality
for(int i = 0; i < to + 1; i++) //Initialize the primality vector
{
bs[i] = bool(i & 1); //even number will be set as false
}
int j = 0;
from = (from <= 3)? 3 : from;
bs[0] = false;
bs[1] = false;
bs[2] = true; //2 is prime
bs[3] = true; //3 is prime
for(int i = 3; i * i <= to; i += 2) //All odds numbers are considering
{
if(!bs[i]) continue; //if the number is not a prime then no need to do
for(j = i + i; j <= to; j += i) // populate all the multiply of i
{
bs[j] = false;
}
}
return bs; //when done, the left number are prime
}

Friday, December 23, 2005

Binary GCD algorithm

จากที่เห็นกันมาแล้วว่า Euclidean algorithm ง่ายต่อการเข้าใจแล้วก็ง่ายต่อการเขียน อีกทั้งยังเร็วกว่าวิธีที่เราคิดกันขึ้นมาเอง แต่ว่า ความเร็วของ Euclidean algorithm ก็ยังไม่สามารถเทียบเท่ากับ binary GCD algorithm ข้อดีอีกอย่างของ algorithm นี้คือสามารถนำมาเขียนบน computer programming ได้ง่ายเช่นเดียวกับ Euclidean algorithm
Binary GCD algorithm มีวิธีการดังนี้
  1. ถ้า a = 0 จะทำให้ gcd(a, b) = b เพราะอะไรก็ตามนำมาหาร 0 ก็ได้ 0 แน่นอนอยู่แล้ว และ b ก็เป็นตัวนึง
  2. ถ้าทั้ง a และ b เป็นเลขคู่ gcd(a, b) = 2*gcd(a/2, b/2)
  3. ถ้า a เป็นเลขคู่ และ b เป็นเลขคี่ gcd(a, b) = gcd(a/2, b) เพราะว่ายังไงซะ 2 ก็ไม่ใช่ ตัวประกอบนึงแน่ ๆ
  4. ถ้า a เป็นเลขคี่ และ b เป็นเลขคู่ gcd(a, b) = gcd(a, b/2) เหตุผลเหมือนข้อ 3
  5. ถ้าทั้ง a และ b เป็นเลขคี่ gcd(a, b) = gcd(abs((a – b)/2), b) = gcd(a, abs((a – b)/2)) เหตุผลมาจาก Euclidean algorithm รวมกะ ข้อ 3 และ 4
เขียนเป็น C/C++ ได้ดังนี้


unsigned int gcd(unsigned int a, unsigned int b)
{
unsigned int c = 0;
while((a & 1) == 0 &amp;& (b & 1) == 0) // both are even number
{
a >>= 1; // divided by 2
b >>= 1; // divided by 2
c++; // power of 2 to multiply back
}

do
{
if((a & 1) == 0)
a >>= 1;
else if((b & 1) == 0)
b >>= 1;
else if(a >= b)
a = (a - b) >> 1;
else
b = (b - a) >> 1;
} while (a > 0);
return b << c;
}


เขียนแบบ Recursive ได้เป็น


unsigned int gcd(unsigned int a, unsigned int b)
{
if(a == 0)
return b;
else if((a & 1) == 0 &amp;& (b & 1) == 0)
return 2 * gcd(a >> 1, b >> 1);
else if((a & 1) == 0)
return gcd(a >> 1, b);
else if((b & 1) == 0)
return gcd(a, b >> 1);
else if(a >= b)
return gcd((a - b) >> 1, b);
else
return gcd(a, (b - a) >> 1);
}



ที่มา และข้อมูลเพิ่มเติม

Binary GCD algorithm at answers.com

Eucledian algorithm at my blog or at my blogger.com

Sunday, December 11, 2005

Greatest Common Divisor

วันนี้กลับไปอ่านหนังสือ discrete math สมัยเรียนมหาลัย ไปเจอบทนึงพูดถึงเรื่อง GCD หรือ Great Common Divisor หรือในไทยก็ ห.ร.ม. หรือ หารร่วมมาก

วิธีการหาหารร่วมมากโดยปรกติ ก็ คือ แยก factor ของตัวเลข 2 ตัวที่ต้องการหา GCD แล้วก็นำมาหารกันจนเหลือตัวที่ไม่สามารถหารกันลงแล้ว

เช่น

GCD(14, 56) = 2x7 and 2x2x2x7 = 1 and 2x7 = 14

วิธีเขียน Program ก็อาจจะทำได้โดย




int GCD(int a, int b)
{
int temp = a;
int temp2 = b;

int gcd = 1;
while((temp % 2 == 0) && (temp2 % 2 == 0))
{
temp /= 2;
temp2 /= 2;
gcd *= 2;
}
for(int i = 3; i <= a && i <= b; i+=2)
{
while((temp % i == 0) && (temp2 % i == 0))
{
temp /= i;
temp2 /= i;
gcd *= i;
}
}
return gcd;
}


แต่มีอีกหลายวิธีที่ทำได้รวดเร็วกว่าวิธีที่เสนอมานี้เยอะมาก วิธีนึงก็คือ วิธี Euclidean วิธีให้ความคิดไว้ว่า GCD ของ a กับ b เท่ากับ GCD ของ b กับ abs(a-b) หรือสามารถเขียนเป็น program ได้ดังนี้



int GCD(int a, int b)
{
if(a % b == 0)
{
return b;
}
else
{
return GCD(b, abs(a - b));
}
}


จะเห็นได้ว่า วิธี Euclidean ทำให้ program ง่ายขึ้นและรวดเร็วขึ้นด้วย

Sunday, October 09, 2005

address ใน C/C++ กับ +, - operator

เวลาที่เราเราแสดงผลตัวแปรที่เป็น pointer ปรกติค่าที่พิมพ์ได้ก่อน dereference จะเป็นค่าตำแหน่งของตัวแปรที่มันชี้อยู่ เช่น

int i = 10;
int *p = &i;

cout << p << endl;

ผลที่ได้จะคล้าย ๆ กะค่าข้างล่างนี้
ffbefa80

ทีนี้มีข้อสงสัยอยู่หนึ่งอย่างคือ ถ้าเราเอาเลขของ pointer 2 ตัวนี้มาลบกันในขณะที่เป็น pointer อยู่ หรือนำค่า pointer มาบวกกับตัวเลขจะเกิดอะไรขึ้น ขั้นแรกเรามาดูก่อนกะ operator + กับ pointer

int i = 10;
int *p = &i;
cout << p << endl;
cout << p + 1 << endl;

ผลลัพธ์ที่ได้ไม่ใช่
ffbefa80
ffbefa81

นะครับแต่ผลที่ได้จะคล้าย ๆ กะค่าข้างล่างนี้
ffbefa80
ffbefa84 คือค่าที่เพิ่มขึ้นมาจะเป็น 1*sizeof(datatype) นั้น ๆ เช่นเดียวกันกับ operator - คือถ้าเอา address ของตัวแปรนั้น ๆ มาลบกันค่าที่ได้ก็ไม่ใช่ ค่าที่เป็น address มาลบกันเฉย ๆ แต่จะเป็น ค่าในลักษณะเดียวกันกับ operator +

มาดู Program ตัวอย่างกันดีกว่า

#include <iostream>
#include <iomanip>

using namespace std;

int main()
{
int a;
int b;

cout << "&a = " << setw(8) << setfill('0') << hex << &a << endl
<< "&b = " << setw(8) << setfill('0') << hex << &b << endl
<< "&a - &b = " << (&a - &b) << endl
<< endl;
return 0;
}

&a = ffbefa80
&b = ffbefa7c
&a - &b = 1

Friday, October 07, 2005

union กะที่ใช้ที่น่าสนใจ

union ในภาษา C/C++ เป็นส่วนที่มีที่ใช้ไม่ค่อยมากมายนัก วันนี้ผมจะมาแสดงที่ใช้งานนึงที่น่าสนใจของ union

ก่อนอื่นก็ขอพูดถีึง union ก่อนนะครับ union เป็น type นึงของ data ก็ตามชื่อคือ มัน union ใช้พื้นที่จัดเก็บร่วมกันใน union type นั้น ๆ ขนาดของมันก็จะมีขนาดเท่ากับ max ของขนาดที่อยู่ใน union นั้น ๆ แต่ถ้ามีขนาดเท่ากัน เราสามารถนำมันไปใช้งานได้ตามตัวอย่างข้างล่างนี้คือ เรารู้ว่า data type นึงของเรามีขนาดเท่าไหร่ เราก็ประกาศ char array ให้มีขนาดเท่ากันเพื่อที่มันจะ share พื้นที่กันได้เต็มที ในตัวอย่างแสดงให้เห็นว่า เราสามารถ แสดง int ออกมาเป็น char array ได้โดยไม่ต้องเขียนอะไรเพิ่มมากเลยเพียงแค่ใช้ union นอกจากนี้เรายังสามารถ demonstrate endianess ของแต่ละ cpu architecture ด้วย

#include <iostream>
#include <iomanip>

using namespace std;
union testunion
{
int i;
char c[4];
};

int main()
{
union testunion a;
a.i = 0x01020304;

cout << setw(8) << hex << setfill('0') << a.i << " : "
<< setw(2) << setfill('0') << hex << (int)a.c[0]
<< setw(2) << setfill('0') << hex << (int)a.c[1]
<< setw(2) << setfill('0') << hex << (int)a.c[2]
<< setw(2) << setfill('0') << hex << (int)a.c[3] << endl;
return 0;
}

Windows Intel (of course)
01020304 : 04030201

Solaris Sparc
01020304 : 01020304

Solaris Intel
01020304 : 04030201


หวังว่า ตัวอย่างนี้จะทำให้เข้าใจการ concept และการใช้งานของ union ได้มากขึ้น

Wednesday, October 05, 2005

XOR เอาไปทำอะไรได้บ้าง

bitwise XOR operator หรือ ^ เป็น operator ที่ไม่ค่อยได้เห็นที่ใช้เท่าไหร่นัก

1st tip วันนี้จะเป็นการใช้ XOR เพื่อทำการ swap ค่าของตัวแปร

ก่อนอื่นก็ขออธิบายว่า XOR มันทำงานอย่างไร XOR operands ทั้ง 2 ตัวถ้าค่าเหมือนกันจะ return false ไม่เหมือนกัน return true

ตัวอย่างเช่น
true xor true ==> false
true xor false ==> true
etc.

หรือถ้าเขียนเป็น 1 กะ 0 ก็จะได้

1 xor 1 ==> 0
1 xor 0 ==> 1

xor มีคุณสมบัติอีกอย่างนึงคือ ค่าใดก็แล้วแต่ ถ้านำไป xor กะค่าอื่น 2 ครั้ง ค่าที่ได้ก็จะกลับคืนไปเป็นค่าเดิม
เช่น
1 xor 0 ==> 1 นำไป xor 0 อีกที ก็จะได้ 1
1 xor 1 ==> 0 นำไป xor 1 อีกทีี ก็จะได้ 1

น่าแปลกนะครับ และนี่ก็เป็นคุณสมบัติที่ทำให้เราสามารถเอาไปใช้เพื่อทำการ swap ค่า

ถ้าให้

a xor b xor b ==> a
b xor a xor a ==> b

มาถึงการ swap ละครับ โดยทั่วไปการ swap ค่าก็จะทำได้โดย

temp = a;
a = b;
b = temp;

เราก็จะได้ค่า a สลับกะ b แต่ในที่นี้เราต้องทำการเพิ่มตัวแปรเข้าไปอีก 1 ตัว

สมมติเรามีตัวแปรแค่ 2 ตัวละครับ เราก็สามารถทำได้ดังนี้ครับ

a = a ^ b; // a' = a ^ b
b = a ^ b; // b' = a' ^ b ซึ่งก็คือ b' = a ^ b ^ b ซึ่งก็เป็น a นั่นเอง
a = a ^ b; // a'' = a' ^ b' ซึ่งก็คือ a'' = a ^ b ^ a ซึ่งก็เป็น b นั่นเอง

นี่เป็นที่ใช้หนึ่งของ XOR

Introduction

Blog นี้ผมตั้งใจจะมีไว้ บันทึก เกร็ดเล็ก เกร็ดน้อย ของการ programming ทุกภาษาที่ผมรู้จัก รวมไปถึง concept ในการคิดด้วย