숫자에 세자리마다 콤마찍는 함수 최적화
글쓴이: saxboy / 작성시간: 월, 2004/07/26 - 9:19오후
요 며칠 외주로 하는 일 하나에서 마무리 단계에 숫자에 세자리마다 콤마를 찍어달라고 하더군요. 별 생각 없이 만들다가 문득 재미있겠다는 생각이 들어 올려봅니다.
방법 1.
3으로 몫과 나머지를 구하고 나머지부분을 먼저 strcpy, 이후 몫만큼 루프를 돌면서 strcpy & 콤마 찍기
그런데, 코드를 만들려고 생각하니 참 일관성이 없더군요. 다시 다른 방법을 생각해보았습니다.
방법 2. 스트링 마지막부터 하나씩 카피하면서 3자리마다 콤마넣어주기.
이렇게 생각해보니 첫번째 아이디어보다는 웬지 깔끔해지더군요. 이렇게 만든 함수가 요놈입니다.
int util_add_comma_to_num(const char *str, char *buf, int buflen) { int len=0; int num_commas=0; int comma_count=0; int i=0; int n=0; /* count given string */ len = strlen(str); num_commas = (int)(len/3); assert( buflen >= (len + num_commas +1)); buf[len + num_commas] = '\0'; for (i=len-1, n=1; i>=0; i--, n++) { buf[i + num_commas - comma_count] = str[i]; if ( (n%3)==0 ) { comma_count++; buf[i + num_commas - comma_count] = ','; } } } #ifdef __TEST__ int main(int argc, char **argv) { char buf[1024]={0,}; util_add_comma_to_num("12345678000", buf, 1024); printf("%s\n", buf); } #endif
굉장히 간단한 코드인데, 역시나 스트링처리이다보니 귀찮은 일이 생기고, 구현 여하에 따라서 웬지 위에 적은 것은 비교도 되지 않을 정도로 깔끔하게 만들 수 있겠다는 느낌이 얼핏들더군요. 다른 분들은 어떻게 만드실지 무척 궁금해져서 스레드를 하나 만들어봅니다. 다른 아이디어 또는 깔끔한 구현! 당신의 능력을 보여주세요.
ps. 웬지 recursive로 만들어도 재미있겠다는 생각이 듭니다.
댓글
http://bbs.python.or.kr/viewtopic.php?t=
http://bbs.python.or.kr/viewtopic.php?t=20504
예전에 본 기억이 있어서 링크를 남겨봅니다. ^^;
위 프로그램은 문자열 길이가 3의 배수일 경우 잘못 동작합니다. :(
위 프로그램은 문자열 길이가 3의 배수일 경우 잘못 동작합니다. :(
그리고 굳이 뒤에서부터 복사할 필요 있나요?
사족: "토론, 토의"보다는 그냥 "프로그래밍 Q&A" 쪽에 올리셨어도...
와~ 굉장한
와~
굉장한 고수이시네요~
^^__^^ 나도 언제묜???
좋은 하루 되세요!!
[quote]위 프로그램은 문자열 길이가 3의 배수일 경우 잘못 동작합니
아유... 창피... :oops:
조금전에 생각하면서는 웬지 뒤부터 복사를 하면 참 깔끔해진다고 생각했는데, 다시 보니 그렇지도 않군요. shift가 참 멋진 것 같아요.
웬지 퍼즐같은 기분이라 토론쪽에 올려두었는데, 별로 적절하지 않을까요? 그렇다면 관리자님께서 옮겨주시면 좋겠습니다.
숫자에 세자리마다 콤마찍는 함수 최적화
예전에 어디선가 본 Duff's device 라는 것이 생각나서... 8)
while loop 내부로의 (goto) switch라.. 상당히 맘에
while loop 내부로의 (goto) switch라.. 상당히 맘에 안 드는 코드로군요. :)
[quote="cdpark"][code:1]intutil_add_co
피연산자가 음수일때 % 연산 결과는 기계마다 다르다고 봤는데...
제가 옛날 책을 보고 있는 건가요??
[code:1]int util_add_comma_to_num(
이건 어떨까요?
[quote="eplus2"]피연산자가 음수일때 % 연산 결과는 기계마다
-2 % 3 에 대해 1을 돌려줄 수도 있고, -2를 돌려줄 수도 있으니깐요. 하지만 -3 % 3 에 대해서는 0을 돌려주겠죠? (면피!)
cdpark님 버전을 약간더 줄여봤습니다.[code:1]/*
cdpark님 버전을 약간더 줄여봤습니다.
한국 BSD 사용자 포럼
최적화란 쓸데없는 계산을 빼는 것.
arith님의 코드는 112,1231,23412,3
arith님의 코드는
1
12
,123
1,234
12,345
,123,456
1,234,567
12,345,678
,123,456,789
와 같은 결과를 내는군요.
한국 BSD 사용자 포럼
[quote="방준영"]cdpark님 버전을 약간더 줄여봤습니다.
좀 더 줄여 볼까요?
---
http://coolengineer.com
Duff 가 누구에요?
위의 Duff's device 스타일의 코드를 다시 좀 더 짧고 보기 좋게 (?) 써 봤습니다. switch () 문의 브레이스도 생략이 가능하다는군요!
방준영님이나 pynoos 님 버전 정도라면 strcpy() 와 꽤나 비슷
방준영님이나 pynoos 님 버전 정도라면 strcpy() 와 꽤나 비슷해져서, 아예 strcpy() 에 맞춰 argument 순서와 반환 타입을 바꿔 봤습니다. 그 외에 사소한 차이점이 좀 더 있습니다. :)
p.s. 개인적으로는 중간에 쓸데없는 비교를 더 이상 하지 않는 Duff's device 쪽이 마음에 듭니다. 물론 코드가 더 괴상하긴 하지만요. :)
[code:1]int util_add_comma_to_num (c
arith 님의 코드가 맘에 드네요. 버그가 있으면 잡으면 되죠.
조금더 고쳐서 0으로 시작, 앞뒤 공백, +/- 부호 까지 처리하게 ^^
조금더 고쳐서 0으로 시작, 앞뒤 공백, +/- 부호 까지 처리하게 ^^;
>/dev/null 2>&1
C++로 해봤습니다. :D [code:1]#include <i
C++로 해봤습니다. :D
소수점이하도 처리하실 분? ;)
소수점이하도 처리하실 분? ;)
---
http://coolengineer.com
[code:1]int util_add_comma_to_num(
꼬리에 덧붙여놓았던 recursive 버전 하나 만들어봅니다.
cdpark님의 것을 제가 수정한 것을 pynoos님이 수정한 것을 다시
cdpark님의 것을 제가 수정한 것을 pynoos님이 수정한 것을 다시 수정한 버전. 마지막에 붙는 buf = '\0';를 삭제했습니다.
Duf's device(?) 코드를 경고 안나게 수정한 버전.
한국 BSD 사용자 포럼
cdpark님 버전을 조금 더 줄여보았습니다.[code:1]v
cdpark님 버전을 조금 더 줄여보았습니다.
좀 더 정확히 얘기하자면
cdpark님 -> 방준영님 -> pynoos님 으로 수정된 버전이지요...
성능은 어떻게 될까요? 무조건 라인수 적다고 빠른건 아니겠죠?[c
성능은 어떻게 될까요? 무조건 라인수 적다고 빠른건 아니겠죠?
제가 초보라, 언뜻 보기엔 이게 젤 빠를것 같은데 아닌가요?
(안전성은 고려하지 않구요)
언제나 시작
arith 님의 코드를 제가 고친 버젼, 혹은 Duff's device를
arith 님의 코드를 제가 고친 버젼, 혹은 Duff's device를 쓴 버젼(결국 둘은 같습니다.) 등이 가장 빠를겁니다.
컴파일러의 최적화 루틴은 소스가 긴 편을 선호하므로 제가 고친 버젼을 좋아할거라고 주장합니다. :)
pentium 4의 깊은 파이프라인을 위해서는 loop를 더 풀어야할지도 모르고요. :)
Duff's device가 뭔가 해서 찾아봤더니 이런 것이었군요.ht
Duff's device가 뭔가 해서 찾아봤더니 이런 것이었군요.
http://info.astrian.net/jargon/terms/d/Duff_s_device.html
정말 멋진 아이디어입니다. 그런데 저만 몰랐나요... :wink:
한국 BSD 사용자 포럼
java 버전...
예전에 만들어 둔것이 있었내요.. :)
멋진남자...
Re: java 버전...
자바는 JDK에서 지원할 텐데 굳이 만드실 필요는... :?
Re: java 버전...
할줄아는건 c 뿐이고..
자바로 만들라하니...
위와같은 코드가 나오내요...
자바 너무 어려워요...
멋진남자...
[quote="doldori"]C++로 해봤습니다. :D [/quote
ANSI C++도 Java 처럼 천단위 컴마 찍기를 지원합니다.
표준 라이브러리의 locale 클래스를 사용하면 되지요.
로케일을 한국어나 영어로 설정하면 자동으로 천단위 컴마 찍기가 됩니다.
(소숫점 이하는 찍히지 않습니다.)
만약 독일어로 설정하면 컴마(,) 대신 피리어드(.)가 찍히죠.
ANSI C에서도 로케일을 써서 이렇게 할 수 있는 지는 모르겠습니다.
제가 C는 잘 몰라서요. :oops:
[quote="cedar"][quote="doldori"]C++로 해봤습
음.. 제가 한번 해봤는데. 안되는데.. 왜 안되는거죠?
ko_KR를 ko-KR, korea 모두 바꾸어 봐도 안돼는군요..
LINUX 환경입니다.
고작 블로킹 하나, 고작 25점 중에 1점, 고작 부활동
"만약 그 순간이 온다면 그때가 네가 배구에 빠지는 순간이야"
In Perl...1. CPAN에서
In Perl...
1. CPAN에서 Tie::Comma 모듈 설치
2. Tie::Comma 패키지가 Export하는 %comma 변수를 사용해서 숫자 변환
3. 음수 소수점 상관없이 동작
---------------------------
Smashing Watermelons~!!
Whatever Nevermind~!!
Kim Do-Hyoung Keedi
----
use perl;
Keedi Kim
Perl 정규식으로
이렇게 구현도 가능해요^^
Gema로 구현하면 ...
--------
설명
<D1> 은 1자리 숫자
<D2> 는 2자리 숫자
<D3> 은 세자리 숫자
<D> 는 자릿수 관계없이 연속된 숫자
모든숫자는 , 찍는 관점에서 보면 3종류로 나누어진다
첫번째는
1,234
1,234,456
처럼 처음의 , 나오기전에 숫자가 1개인것
두번째는
12,345
12,344,567
처럼 처음의 , 나오기전에 숫자가 2개인것
그리고 마지막으로
123,456
123,456,789
처럼 처음의 , 나오기전에 숫자가 3개인것
그리고 처음 콤마를 포함한 뒷자리는 , 와 3자리 숫자의 반복이다.
<CDR> 은 이 3자리 숫자의 반복을 패턴으로잡아내어 그 사이에 , 를 붙인다.
<CDR> 은 어떻게 3자리 숫자를 찾는가?
CDR 은 3개의 룰로 구성되어있는데
첫번째는
<D3>=,$1
3자리 숫자를 만나면 숫자앞에 , 를 붙인다.
두번째는
<D>=@fail
숫자를 만나면 지금 처리하는 문자는 CDR 패턴에 맞지 않는다라고 알려준다.
세번째는
=@end
성공적으로 처리를 끝낸다. (지금 처리한 문자열은 CDR 패턴이다.)
12345678 을 변화하는 예를 들면
맨처음 <D1><CDR>에 12345678 이 맞는지 비교한다.
<D1>=1
<CDR>=2345678 이어야 패턴에 맞는데
CDR 에 2345678 을 매칭시켜보면
(룰 적용 순서는 먼저정의된 룰먼저 적용하고 룰이 적용되면 이후 스트링은 룰의 처음부터 다시적용된다)
CDR 의 처음룰에 의해 234 이 ,234 으로 변화되고
나머지 45678 에 대해서
CDR 룰의 처음부터 다시적용하면 567 이 ,567 으로 변환되고
룰이 적용되었으니 나머지 8 에 대해 CDR 룰의 처음부터 다시적용하면
첫번째 룰은 3자리수 이어야 하는데 남은것은 8 하나이니 첫번째 룰은 실패
두번째 룰을 적용하면 숫자일경우 @fail 인데 8은 숫자이므로 여지껏 변환한거 롤백함 실패응 돌려줌
다시 처음으로 돌아와서
결국 <D1><CDR> 패턴이 CDR 에서 실패함으로써 결국 맞지않았으므로
12345678 을 두번째 룰 <D2><CDR>을 적용해 본다.
그러면 아까와 같은 방법으로 <D2> 에 해당하는 12 를 제외한 345678 을 <CDR> 에 적용해보면
CDR 의 첫번째 패턴이 두번적용되면 세번째 적용할때 CDR 의 세번째 룰 =@end 에 의해 성공적으로 매칭이 끝난다.
그러면 <D2><CDR>=$1$2 에 의해 D2 -> 12 CDR -> ,345,678 로 치환된다.
-------------------------------------------------------------------
그리고... Gema 쓰시는분 어디 없나요?
빠르지만 않지만 짧은 Haskell 버전
간단하죠?
제가 쓰는 java버전...
import java.text.DecimalFormat; // 숫자를 원하는 형식으로 바꿔 문자열로 바꿔주는 API
import java.io.*;
public class Money
{
public static void main(String args[]) throws IOException
{
...... 변수명들 생략 ......
String checkDay = null;
String checkMoney = null;
....입력받은 것이 숫자인지 체크하는 부분 생략......
checkDay = new DecimalFormat("#,###").format(y);
checkMoney = new DecimalFormat("#,###").format(saveMoney);
......중간 왕창생략..^^......
}
이런식으로 사용중입니다. 변수명 'y'에는 날짜를 받아와서 '1,234' 일... 이런식으로 찍어주고 'saveMoney'에는 누적된 합계금액을 써서 '1,234,856' 원... 이럭식으로 콤마를 찍어주고 있죠. 원래 예전 쓰레드에 글 올린적이 있는 하루에 10원씩 늘여가면서(첫날 10원, 둘째날 20원 이런식...) 저금하면 키보드로 입력받은 날짜의 누적금액은 얼마인가하는 호기심에 15분간에 걸쳐 뚝딱거려 만든 소스중 일부였죠.^^
예전 1.3때는 만들어 썼는데 1.4때부터 DecimalFormat 로 사용하니 편하더라구요.^^
-------------------------------------------------------------------------------------------
나에겐 할 수 있다는 의지와
하면 된다는 신념과
해야 한다는 의무가 있다.
http://rx78gd.egloos.com
-------------------------------------------------------------------------------------------
나에겐 할 수 있다는 의지와
하면 된다는 신념과
해야 한다는 의무가 있다.
http://rx78gd.tistory.com
Duff's device 코드와 제 코드가 거의 똑같네요.
몇달전에 봤던 한글과컴퓨터 면접 실기테스트 3번문제였던가. 이 문제였습니다.
> -100000
>> -100,000
> +1000000
>> +1,000,000
> 100000
>> 100,000
신기하네요. 랭귀지가 같은 이상 길은 하나인가. 매번 느끼는거지만. 모든 길은 로마로 통한다!!
제 개인적인 생각으로 루핑을 푸는것도 중요하지만, 루프안의 조건문을 최대한 줄이는거죠.
루프안의 조건문은 최적화를 방해하는 요소인듯 합니다.
이 문제 특성상 플밍스킬이 조그만 있어도 쉽게 풀수 있지만. 루프->조건
조금 생각해보면 루프안의 조건을 쉽게 풀수 있습니다.
Hello World.
파일정리하다 찾았네요.
작년에 한글과컴퓨터 면접용 1번문제였네요. 그 당시 컴이 갑자기 맛이가서리 하루밖에 시간이 없어서 6문제중 5문제만 풀어서 제출했는데 나중에 문서를 잘 일어보니까. 한문제당 20점 총 120점, 100점이하는 무조건 탈락. ㅡㅡ;
결국 5문제를 만점 받아도 한문제라도 못풀면 자동탈락이었다는..ㅋㅋㅋ
주석이 빠진걸 보니까. 최종 제출본은 아니었네요. 찾으면 다시 수정하겠씁니다.
#define CIPER (float)(3.0)
#define MAGIC_NUMBER (float)(0.7)
두 값을 수정하면 자릿수 변경이 가능합니다,
지금와서 보니 위의 더프의 디바이스와 거의 비슷하죠!? ^^; 이런 사실을 소스 냈을때 한컴팀장들은 알았으라나..ㅋㅋㅋ
Hello World.
shell script에서는...
shell script에서는 함수로 아래처럼...
이런 코드도 괜찮지
이런 코드도 괜찮지 않나요..
void add_comma(const char *str, char *buf, int buflen)
{
while(buflen > 3)
{
*buf++ = *str++;
*buf++ = *str++;
*buf++ = *str++;
*buf++ = ',';
buflen -= 3;
}
for(; buflen > 0; buflen--)
*buf++ = *str++;
*buf++ = 0;
}
에휴.. 아무생각
에휴.. 아무생각 없이..잘못 생각했네요..
위에서 부터 3개 단위로 점이 찍히게 프로그램 됐네요.. ㅋㅋ
지워야 할건데 지울 방법을 모르겠네요...ㅠㅠ
참고하세요..
http://groups.google.co.kr/group/comp.lang.c/msg/165f11c5f832321e?
--
Linux강국 KOREA
http://ydongyol.tistory.com/
--
Linux강국 KOREA
http://ydongyol.tistory.com/
사고의 확장 1. 첫
사고의 확장
1. 첫 글자가 +, -인가?
2. 뒤집어 역순으로
3. 소숫점을 찾는다
4. 세 글자마다 쉼표
5. 다시 뒤집어 정순으로
라는 망상을 해봤습니다.
자 이걸 2와 5 과정을 줄여서 효율적으로 만들면 됩니다. :)
돼지군 작업실 Revision E: E-Prot, Eightbyte OS, ...
F/OSS를 위해 뭔가 하고 싶습니다. 뭘 하면 좋을까요?
Real programmers /* don't */ comment their code.
If it was hard to write, it should be /* hard to */ read.
memmove 버전 /** s는
memmove 버전
---------
간디가 말한 우리를 파괴시키는 7가지 요소
첫째, 노동 없는 부(富)/둘째, 양심 없는 쾌락
셋째, 인격 없는 지! 식/넷째, 윤리 없는 비지니스
이익추구를 위해서라면..
다섯째, 인성(人性)없는 과학
여섯째, 희생 없는 종교/일곱째, 신념 없는 정치
---------
간디가 말한 우리를 파괴시키는 7가지 요소
첫째, 노동 없는 부(富)/둘째, 양심 없는 쾌락
셋째, 인격 없는 지! 식/넷째, 윤리 없는 비지니스
이익추구를 위해서라면..
다섯째, 인성(人性)없는 과학
여섯째, 희생 없는 종교/일곱째, 신념 없는 정치
오늘 따라 잠이
오늘 따라 잠이 안오는 군요. 예전 글이지만 다시 앞으로 올라와서 한번 살펴보니
common lisp 버전이 없는 듯하여 답글 하나 추가해봅니다.
common lisp은 그냥 format directives에서 지원됩니다.
장점은... 언제나 그렇듯 문제에 집중할 수 있습니다. ^^;
http://gigamonkeys.com/book/a-few-format-recipes.html
-----
오늘 나의 취미는 끝없는, 끝없는 인내다. 1973 法頂
-----
오늘 나의 취미는 끝없는, 끝없는 인내다. 1973 法頂
뭐, C나 C++ 코드들은
뭐, C나 C++ 코드들은 워낙 잘 작성들을 해놓으셔서, 그냥 지나가다가 Harbour(Clipper)라는 언어로 구현해봅니다.
時日也放聲大哭
時日也放聲大哭
man strfmon(3)
man strfmon(3)
PERL로 한줄코드 해봤어요 ^^
1 while ($Num =~s/(.+)(.{3})/$1,$2/);
생각해보니 틀렸네요 다시 수정합니다.
1 while ($Num =~s/(\d+)(\d{3})/$1,$2/);
댓글 달기