C++ 까기

모 채널, 2010년 3월 31일:

<nidev> http://www.cplusplus.com/reference/std/locale/collate/hash/
<nidev> 음
<nidev> lifthrasiir: low 와 high에 대해서 설명해주실 수 있으신가요
<lifthrasiir> nidev: begin() end()랑 똑같네요
<nidev> 음
<lifthrasiir> 왜 std::string 잘 보시면 begin() end()도 있고
<lifthrasiir> 생성자에 반복자 두 개 받는 것도 있잖아요 그거랑 똑같음
<lifthrasiir> 반복자에 대한 설명이 필요하신지
<nidev> 뭔 자 뭔 자 말고
<nidev> 그냥 영어로 써주세요
<nidev> 반복자는 repeater?
<__khris__> [low,high) 음 이거 하나로 설명이 가능한...
<lifthrasiir> iterator
<__khris__> 이터레이터
<nidev> ....
<lifthrasiir> nidev: 그러니까 이터레이터라는 건 포인터의 일반화인데요,
<nidev> 넹
<lifthrasiir> 배열 int arr[5]가 있다고 치면
<lifthrasiir> 이 배열의 첫 원소는 arr + 0이고 끝원소는 arr + 4잖아요
<nidev> 네
<nidev> 어드레스가 그렇게 되죠
<lifthrasiir> 근데 끝원소 대신에 우리는 보통 past-the-end를 쓰는데 그러면 past-the-end는 arr + 5죠
<lifthrasiir> past-the-end를 쓰는 이유는 다루기가 더 편하다는 것도 있고 배열 인덱스가 그렇게 붙여지는 것도 있고
<lifthrasiir> 마찬가지로 어떤 문자열... std::string p라고 칩시다
<lifthrasiir> 그럼 이거의 시작 원소는 p.begin()이고 past-the-end는 p.end()죠
<lifthrasiir> 일반화했다는 건 저게 실제로 배열처럼 안 생겼어도 일정한 연산자 정도만 지원하면 똑같이 쓸 수 있다는 얘기가 되거든요
<lifthrasiir> 예를 들어서 std::map은 절대로 배열로 구현되지 않는데...
<lifthrasiir> for (std::map<int,int>::iterator it = map.begin(); it != map.end(); ++it) ...
<lifthrasiir> 식으로 배열과 똑같이 순회할 수 있죠
<lifthrasiir> 배열이라면 이렇게 될텐데:
<lifthrasiir> for (int *p = &arr[0]; p != &arr[5]; ++p) ...
<랜덤여신1> 언제 봐도 토 나오는 이터레이터~
<랜덤여신1> auto~ auto~ auto~~~
<lifthrasiir> 랜덤여신1: 그렇지 C++가 최악임
<랜덤여신1> C++0x~~~~~~
<lifthrasiir> C++0x는 개뿔
<lifthrasiir> C++0x가 들어 와도 망할 거야
<랜덤여신1> auto 무시하나요
<lifthrasiir> auto만으로 해결 안 되는 설계상의 문제가 한가득
<랜덤여신1> 예를 들어 어떤 건가요
<랜덤여신1> 무슨 예가 있는지 말해보세요
<lifthrasiir> 일단 타입 시스템이 거지같아요
<랜덤여신1> 자바랑 동일한 형 시스템 아닌가요
<d3m3vilurr> 거지같은 타입시스템?...????
<lifthrasiir> 자바랑 동일하다니...
<lifthrasiir> 자바를 무시하나
<__khris__> 절대아닌..
<랜덤여신1> 어떤 게 다른가요
<peremen> 자바에 대한 모욕인가 c++에 대한 모욕인가
<__khris__> 처음부터 끝까지
<__khris__> 자바에대한.
<lifthrasiir> C++ 메타프로그래밍 해 봤나요
<lifthrasiir> peremen: 자바에 대한 모욕
<랜덤여신1> 템플릿 메타프로그래밍을 말하나요
<d3m3vilurr> peremen: 둘다 서로 싫어할듯
<lifthrasiir> 물론 그것만으로 끝나지 않고
<lifthrasiir> dispatch에서도 웃기지도 않은 것이 있고
<nidev> 음...
<lifthrasiir> 내가 짠 코드 중에 뭐가 있더라... 잠시만
<lifthrasiir> 랜덤여신1: http://cosmic.mearie.org/tmp/gaea/include/meta/types/same.h
<랜덤여신1> 무엇을 봐야 하나요
<lifthrasiir> 저건 물론 boost에서 상당한 영향을 받은 코드지만
<lifthrasiir>   template <class T> ::gaea::meta::detail::types::YES helperForIsSame(T*, T*);
<lifthrasiir>   ::gaea::meta::detail::types::NO helperForIsSame(...);
<lifthrasiir> 이 두 줄은 서로 다른 함수를 지칭합니다
<lifthrasiir> 그리고 그 반환 타입은 물론 argument에 따라 달라집니다
<랜덤여신1> 그건 당연한 거 아닌가요
<lifthrasiir> argument를 dispatch하기 위하여 컴파일러는 unification(니가 무슨 프롤로그니)을 수행해야 합니다
<lifthrasiir> 이 unification 알고리즘은 상당히 많은 팩터를 갖고 있는데
<lifthrasiir> - 물론 primitive type에 따라서 달라져야 하고...
<lifthrasiir> - 포인터, 레퍼런스, "배열"(은 다른 타입임)
<lifthrasiir> - 클래스 공용체 구조체 (까지는 일단 봐 준다.)
<lifthrasiir> - const, volatile (이걸 C++에서는 묶어서 CV qualifier라고 부르는데 이거의 처리가 굉장히 복잡함)
<lifthrasiir> CV는 깔 거리가 여러 가지가 있는데 (규칙이 너무 복잡함) 일단 그냥 간단하게 설명할께요
<lifthrasiir> g++가 CV를 제대로 구현 못 합니다
<lifthrasiir> LLVM clang이 이걸로 g++를 깠죠 우리는 CV 완벽하게 구현한다고
<lifthrasiir> (물론 얘네가 벤치마크할 때 쓴 테스트 슈트는 내 기억이 맞다면 generated... 케이스가 천 몇백개...)
<nidev> .....
<lifthrasiir> 솔직히 말하면 이건 g++도 LLVM도 탓할 게 아니라 C++ 탓이죠
<lifthrasiir> 자 CV 얘기는 무시하고 더 나가 볼까
<lifthrasiir> - 함수 포인터, 멤버 함수 포인터, 멤버 변수 포인터, 등등등
<lifthrasiir> 재밌는 거 하나...
<lifthrasiir> 멤버 함수 포인터의 크기가 컴파일러에 따라 다르다는 것을 알고 계십니까
<lifthrasiir> (같은 플랫폼, 이를테면 x86인데도)
<nidev> ??
<랜덤여신1> 그것도 당연한 거지요
<lifthrasiir> 그럼 같은 컴파일러에서도 케이스 바이 케이스로 다르다는 것을 알고 계십니까
<랜덤여신1> 표준에 없으니까요
<lifthrasiir> 예를 들어서 Visual C++는 (내 기억이 맞다면)
<lifthrasiir> forward declaration만 한 클래스의 멤버 함수 포인터와
<lifthrasiir> declaration이 나온 뒤 똑같은 클래스의 멤버 함수 포인터가
<lifthrasiir> 크기가 다릅니다
<랜덤여신1> 선언을 미리 어떻다고 가정할 수 없으니 그러겠지요
<lifthrasiir> 애당초 one-pass로 declaration을 처리하는 것부터 문제가 있는 거 아닌가
<lifthrasiir> 실제로는 one-pass도 아니지
<lifthrasiir> C++ 파서는 일반적인 LL(1)로 표현 못 합니다
<랜덤여신1> 그런 거는 static_cast로 해결되는 거 아닌가요
<lifthrasiir> 안 되는 걸로 알고 있습니다
<랜덤여신1> 표준이 어떤지는 모르겠지만 static_cast로 되어야 할 것 같아요
<lifthrasiir> C++ 파서의 일반적인 구조는
<lifthrasiir> 1. 파싱을 하다가
<lifthrasiir> 2. 심볼에 dependent한 부분이 있으면
<lifthrasiir> (실제로 이런 부분이 두 세 군데 있음)
<lifthrasiir> 3. 일단 가능한 형태로 파싱한 뒤
<lifthrasiir> 4. semantic analysis를 할 때 그걸 다시 체크해서 에러가 있는지 확인합니다
<lifthrasiir> 이게 뭔 개소리냐 하면
<lifthrasiir> 예를 들어서 우리가 C++ 코드를 정적 분석을 할라고 해요
<lifthrasiir> 아니 정적 분석도 필요 없다
<lifthrasiir> 단순한 분석을 할려고 하는데 전체 컴파일러 슈트가 필요한 거죠
<lifthrasiir> 왜냐하면 파싱 스텝이 다른 스텝과 분리가 전혀 안 되니까
<lifthrasiir> 계속 말해 볼까
<lifthrasiir> 단순한 분석이라는 게 이를테면 aspect라거나 그런 걸수도 있어요 (얘네는 보통 패턴으로 binding을 할 수 있으니까)
<lifthrasiir> aspect를 쓰려면 파싱만 하면 되죠 semantic하게 접근할 필요가 별로 없는데
<lifthrasiir> 컴파일러 슈트를 다 구현해야 한다면 좆망이지
<nidev> 음...
<랜덤여신1> 그건 형 시스템 문제와는 좀 별개 아닌가요
<lifthrasiir> 사실 이래서 EDG 같은 애들이 잘 나가는 것이지만... (얘네는 C++ 컴파일러 슈트를 팜)
<lifthrasiir> 랜덤여신1: 형 문제를 얘기하면서 곁다리로 얘기합니다
<lifthrasiir> 그냥 전체 토픽이 "C++가 왜 개같은가"라고 이해하세요
<lifthrasiir> 전체 토픽을*
<lifthrasiir> C++의 형 시스템이 왜 개같은가가 아니라
<랜덤여신1> 자바에는 템플릿이 아예 없으니 C++ 템플릿 관련 문제로 형 시스템이 전혀 다르다고 얘기하는 건 이상해요
<lifthrasiir> 자바는 템플릿의 complexity를 적절한 방법으로 덜어 내는 방법을 고안해 낸 거죠
<lifthrasiir> 그리고 그거 아시나요
<lifthrasiir> template과 generic은 보기에는 비슷해 보이지만 실제로는 완전히 다릅니다
<lifthrasiir> 구현이 다르다는 소리가 아니라 generic으로 되고 template으로는 안 되는 케이스가 있음
<랜덤여신1> 템플릿 메타프로그래밍이라는 것이 존재하는데 어떻게 비슷하겠어요
<__khris__> TMP는 의도된게 아님
<lifthrasiir> 그러니까 generic으로 되고 템플릿은 안 되는 게 있다고...
<랜덤여신1> 일반적인 사용에서 말인가요?
<lifthrasiir> generic의 가장 중요한 특징은 멀쩡한 형태의 parametrized type을 제공한다는 겁니다
<lifthrasiir> 템플릿은 그걸 다른 형태로 구현하려고 하지만 실제로는 완전하지 않아요
<lifthrasiir> 왜냐하면 템플릿은 subtype을 보존하지 않거든요
<lifthrasiir> 우리는 이를테면 (C++ 문법을 빌리자면) A가 B의 subtype이면 Foo<A>가 Foo<B>의 subtype임을 예상하지만 템플릿은 이 예상을 전혀 보장해 주지 않아요
<랜덤여신1> 옳은 것 같군요
<lifthrasiir> 물론 좀 삽질하면 비슷한 걸 하려고는 할 수 있겠지만
<lifthrasiir> 근데 내가 왜 랩돌이에 와서 C++ 한탄을 하고 있어야 하나
<lifthrasiir> 아오 짜증나
<nidev> 모든 원인은 c++입니다
<lifthrasiir> CV 말고 또 뭔 얘기를 하다가 여기까지 왔나... 모르겠다
<랜덤여신1> 여러 가지 유익한 말씀 고맙습니다
<lifthrasiir> 눼
<lifthrasiir> 마지막으로 한 마디만 하자면
<lifthrasiir> 누가 C++가 복잡해진 원인을 C의 문법과 의미를 보존한채 확장을 하려고 해서 어쩔 수 없이 그렇게 되었다고 하지만
<랜덤여신1> 확장을 그렇게까지 하려는 생각이 잘못되었어요
<lifthrasiir> C++의 예상은 완전히 틀렸어요
<lifthrasiir> C와 완벽하게 interoperable하면서 C 문법이 아닌 언어는 굉장히 많습니다
<lifthrasiir> (사실은 대부분의 C 아닌 언어가 C FFI를 결국 어떤 형태로든 지원하기 때문에...)
<lifthrasiir> C++와 그나마? 비슷하면서 C 문법을 안 쓰는, 하지만 C 호환이 굉장히 잘 되는 언어의 예로는 D가 있고
<lifthrasiir> C++와 전혀 안 비슷하면서 여전히 C 호환이 잘 되는 언어의 예로는 Haskell이 있습니다
<lifthrasiir> 이걸로 설명이 될 듯
<lifthrasiir> 둘 다 사려깊게 설계된 언어이면서 C++의 함정을 굉장히 잘 피해 갔죠
<lifthrasiir> C++가 그렇게 하지 못 했다는 것은 이해가 안 갑니다. 아마 일부러 안 했겠죠. 사람들 골탕먹이려고 (진담 반)
<랜덤여신1> 스트롭스트룹이 원한 것은 문법 호환성이 아니라 동작 호환성이었나요?
<lifthrasiir> 잘 모르겠군요
<랜덤여신1> 전자가 진짜 의도라면 스트롭스트룹은 뭘 예상한 것도 아니고 그냥 자기 만들고 싶은 걸 잘 만든 것 같아요
<__khris__> TMP가 의도되지않은 기법임에도 상당히 중요한 기법으로 여겨진다는것 또한 C++이 얼마나 카오스인가 증명하는 사례가 되겠군요
<lifthrasiir> __khris__: 왜냐하면 다른 방법으로 못 하니까...
<lifthrasiir> 사실 boost가 굉장한 건 TMP라는 어처구니 없는 기법을 어떻게든 잘 사용하고 있거든요
<__khris__> 그렇죠
<lifthrasiir> 물론 거기에 안주한다는 점에서 한계는 분명하지만
<__khris__> Loki는 전부 TMP 예제고
<lifthrasiir> 디자인 패턴이라는 것은 언어에서 지원이 안 되는 것을 패턴(= 언어 사용자들의 뇌리에 각인된 유사 문법)으로 해결하려는 것인데
<lifthrasiir> 디자인 패턴이 굉장히 많이 발달한 언어가 C++라는 걸 생각해 보면
<lifthrasiir> 언어의 deficiency는 뻔하죠
<lifthrasiir> (물론 자바도 많은 편이긴 한데... 자바도 다른 방향으로 언어 차원 기능이 적긴 하지만 C++만큼은 아님)
<__khris__> 지금까지 ISO C++ 최종안(0x말고) 다 만족하는 컴파일러도 없지않나요?
<lifthrasiir> 만약에 fully conforming이라고 한다면 없어요
<lifthrasiir> EDG C++가 export 지원으로 거의 유사한 수준에 간 것 같긴 하지만 그게 conform인지 아닌지를 판단할 주체가 일단 없으니까
<__khris__> 템플릿 지원이 특히나  매우 문제인데
<lifthrasiir> (이를테면 POSIX 표준을 특정 OS+유저랜드 소프트웨어 조합이 만족하는지 어쩌구 하는 건 그걸 증명할 만한 그룹이 있는데... ISO C++는 아님)
<lifthrasiir> Open Group이었나에서 certificate를 줄 거에요 만족하는 곳에는
<lifthrasiir> 물론 certificate는 비쌈 (...)
<__khris__> 결론은 얼른 PySide가 안정화돼서 심비안에서 PySide를 쓰고싶습니다(...)
<lifthrasiir> 뭔가 결론이 이상하군여(...)
<__khris__> 심비안은 일단 C++이니까요
<__khris__> Qt로 한다해도 C++
<__khris__> 아 짜증이 막...
<lifthrasiir> C++ 잘 깠다
<lifthrasiir> 오랜만에 개소리 좀 했더니 저널에 글을 쓰기 싫네
<__khris__> 복붙하세요
<lifthrasiir> ;
<peremen> 마치 변비가 해결된 맆을 보고 계십니다
<__khris__> C++표준에서 지랄맞은걸 Qt로 커버해줘도 커버하는법 매뉴얼 뒤져보다가 빡치고
<lifthrasiir> Qt는 C++를 제 나름대로 확장하죠 또...
<__khris__> 네
<peremen> moc
<__khris__> qmake
<lifthrasiir> 내 기억이 맞다면 signal/slot일텐데 또 있나요 혹시
<__khris__> foreach
<__khris__> 키워드
<__khris__> ㅋㅋㅋㅋ
<lifthrasiir> 키워드;
<lifthrasiir> signal/slot은 참고로 boost에 있음
<lifthrasiir> 물론 전혀 아름답지 않습니다
<lifthrasiir> C#과 비교해 봐 그냥
<__khris__> 뭐 이쪽이야
<__khris__> Qt만들어졌을때가 C++구현체들이 표준을 따라잡긴커녕 개뿔이었을때 만들어졌다는점과
<__khris__> Boost나 Loki처럼 한계를 드러내지 않기위해...
<lifthrasiir> 하여튼 C++가 나쁜 놈
<__khris__> 그러고보니 TMP사용시 주의할점에
<__khris__> 니 컴파일러가 좆같아서 이 TMP트릭이 안통할지몰라
<__khris__> -_-
<__khris__> 물론 지금까지 그런경우를 만난적은 없지만
<peremen> 트롤텍 기술자들은 존경해야함
<__khris__> 뛰어난 트롤들이예요
<lifthrasiir> __khris__: boost는 그걸 다 어떻게 workaround로 잘...
<__khris__> 아 텍스트 스트림에서 readLine하면 왜 한글자씩 얻어오는거죠?
<__khris__> -.-;
<lifthrasiir> boost::function1, boost::function2 따위 하는 것도 있고
<lifthrasiir> ("alternative syntax"로...)
<__khris__> 사실 function1 function2 하는거부터 에러잖아요...
<__khris__> ...
<lifthrasiir> 그지같죠
<__khris__> 욕나오죠
<__khris__> 모오더언 C++디자인 보는데
<lifthrasiir> 사실 타입 시스템이 복잡해도 그게 좀 consistent하면 할만한데
<__khris__> 템플릿 괄호겹치면 빡치므로 매크로를 만든다 한 15개까지만들면되겠져
<__khris__> 라고 하면서 저런식으로 15개를만드는데
<lifthrasiir> implicit conversion이 거지임
<__khris__> 보면 진짜 빡치죠
<lifthrasiir> __khris__: 그걸 또 PP로...
<__khris__> 이게 모던인가
<peremen> >>
<peremen> > >
<lifthrasiir> peremen: 뭐 그 정도는 봐 줄 수 있음
<__khris__> 포스트 모던 C++ 디자인이 나와야되는게 아닐까
<lifthrasiir> 내 생각에는 maximal munching rule 자체는 sane함
<lifthrasiir> trigraph는 안 sane함
<nidev> 응
<__khris__> 괜찮아요
<nidev> 그냥 C스타일 캐스팅해야지
<nidev> 모르겠다
<__khris__> 포스트 모던은 원래 까이라고 있는거예요
<lifthrasiir> ㅋㅋㅋㅋㅋㅋㅋ
<__khris__> 저게 제일 싫어요
<__khris__> C스타일캐스팅
<__khris__> ㅠㅠ
<__khris__> 하지만 편하지
<__khris__> 깔깔깔!
<nidev> __khris__: static_cast<>는 어떻게 쓰는건가요
<nidev> 깔깔깔!
<__khris__> 언사인드 사인드 워닝이 났어
<__khris__> C스타일 캐스팅
<lifthrasiir> __khris__: ::foo::some_template_type<::foo::some_template_argument>
<lifthrasiir> 이러면 망합니다
<__khris__> 깔깔깔!
<__khris__> 우웨에엑
<nidev> 뭐야 무서워
<__khris__> 김재귀
<lifthrasiir> <:가 digraph
<peremen> 흠좀무
<lifthrasiir> ㅆㅂ
<__khris__> 아 재귀는 ㅇ ㅏ니군
<__khris__> 배경색이 달라져서 잘못읽었다
<lifthrasiir> <랑 ::를 띄워야 함
<lifthrasiir> 병신같음
<__khris__> ㅇㅇ...
<peremen> 그런데 digraph는 끌 수 있지 않나
<__khris__> 그리고 typename적어줘야되는 특정상황
<peremen> 아니 trigraph였나
<lifthrasiir> peremen: portable하지 않지 그런 가정에 의존하는 건
<__khris__> 그것도 좀 짜증
<lifthrasiir> __khris__: 아....으ㅇ....
<lifthrasiir> ADL도 문제에요
<lifthrasiir> 컴파일러 만드는 인간들이 그거 때문에 짜증을 내죠
<lifthrasiir> O(n^2) 된다고 (...)
<__khris__> ...
<nidev> 오 예쁘다
<nidev> 앞으로 C스타일은 안써야지
<lifthrasiir> scalable compiler를 만들려면 O(n log n) 이상 알고리즘을 안 쓰는 게 관례인데
<nidev> 근데 앞에 static_cast치기가 귀찮은데
<__khris__> 대신 길죠
<__khris__> 네
<lifthrasiir> 언어 설계상으로 O(n^2)를 강제하는 게 있으면
<lifthrasiir> 좆망
<lifthrasiir> 심볼 엄청 많은 소스코드 컴파일이 오래 걸리는 이유 중 하나입니다
<__khris__> 그래서 스캇 마이어스가 우스개소리로 '이건 캐스팅은 쓰지말라고 해놓은거예요'
<peremen> ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ
<lifthrasiir> 참고로 O(n log n)이 왜 limit인지는 아시는 분은 아시리라 믿음
<lifthrasiir> ....
<__khris__> <- 모르는사람
<__khris__> ...
<lifthrasiir> __khris__: 일단 소팅이 있고요
<__khris__> 냅
<lifthrasiir> 물론 컴파일러에서 소팅을 할 일은 없지만
<nidev> 속팅
<peremen> ;c2 thrdoxld
<프리나리안> <p·eremen> 속애팅
<__khris__> 소개팅 이라고 드립안해야지
<lifthrasiir> 소팅을 내부적으로 사용하는 거의 모든 자료구조나 알고리즘들이 O(n log n)을 사용함
<__khris__> 아 제길 저럴줄알았어
<lifthrasiir> 이미 했잖...
<nidev> function list 뽑아서 소팅할 일이 있을진 모르겠지만
<nidev> 음
<nidev> 아무트
<nidev> 아무튼
<lifthrasiir> 예를 들어서 balanced tree는 삽입 순서에 관계 없이 멀쩡한 트리를 만들기 위해 존재하잖아요
<__khris__> 그렇죠
<lifthrasiir> 근데 balanced tree는 일반 binary tree의 특수 케이스죠
<__khris__> 안그럼 좃망이니까..
<lifthrasiir> unbalanced binary tree도 삽입 순서만 잘 맞추면 balanced로 쓸 수 있어요 (그 뒤는 또 폭발이지만)
<lifthrasiir> 근데 삽입 순서를 맞추는 과정 자체가 소팅을 동반합니다
<lifthrasiir> ~끝~
<lifthrasiir> 요컨대 제가 하려는 얘기는 저런 알고리즘들은 사실 소팅을 안 써도 잘 풀어 보면 소팅이 내부적으로 잠재되어 있고
<lifthrasiir> 그래서 O(n log n) 복잡도가 나오는 경우가 굉장히 많다는 거에요
<lifthrasiir> 언어 파싱할 때 왜 LL(1)이나 LALR(1)을 그렇게 강조하는 거냐 하면
<lifthrasiir> 역시 이거에 따라서 파싱 복잡도가 달라지기 때문이죠
<__khris__> 근데 C++은 전혀 만족하지 못하고
<lifthrasiir> 일반적인 context-free grammar를 파싱하려면 O(n^3) 시간이 필요합니다 (CYK algorithm)
<__khris__> gcc팀은 울고 clang팀은 x빠지게 최적화하는거군요
<lifthrasiir> 하지만 LL(1)이나 LALR(1)은 O(n)에 되죠
<lifthrasiir> 그래서 쓰는 건데 C++는 이걸 그냥 개무시ㅋ
<__khris__> 그렇구나..
<__khris__> context-free면 자연어같은거겠네요
<lifthrasiir> 물론 O(n)도 상수 따라 다르지만.. 일단은 비슷한 클래스라고 쳐 두면...
<lifthrasiir> __khris__: 자연어는 context-sensitive로 추정됨
<__khris__> 아
<__khris__> 문법은 있지
<lifthrasiir> context-free라는 건... 이를테면,
<__khris__> 신적 방언?-_-;
<lifthrasiir> for (int i = 0; i < 10; ++i) 같은 게 well-formed라고 하자고요
<lifthrasiir> 그럼 우리가 여기서 "i"를 "j"로 고쳐도 (말은 안 되지만) well-formed죠
<__khris__> ㅇ
<lifthrasiir> 주변 문맥과 상관 없이 심볼을 (일정 규칙에 따라) 바꿔치기 할 수 있다는 게 문맥 독립의 개념임 (물론 구체적으로는 잘 정의해야 하지만...)
<lifthrasiir> 자연어는 항상 그런 게 아니죠
<lifthrasiir> 자연어까지 갈 필요 없이
<nidev> ㅎ므
<lifthrasiir> abc, aabbcc, aaabbbccc, ... 등으로 구성된 언어를 생각하면...
<lifthrasiir> 얘는 context-free가 아닙니다. 저기에 속하는 문자열 중 일부를 어떤 방법으로 치환해도 항상 속함을 보장할 수가 없어요.
<lifthrasiir> 항상 그 결과가 이 언어에 속함을*
<__khris__> 흠
<lifthrasiir> 관심 있으시면 pumping lemma를 찾아 보셈
<lifthrasiir> 영어라 문제지만
<__khris__> 매우 큰 문제군요

일러두기: 이 대화 기록의 신뢰성 따위는 보장하지 않습니다 직접 맞는지 찾아 보세요


ikiwiki를 씁니다.
마지막 수정