디시인사이드 갤러리

마이너 갤러리 이슈박스, 최근방문 갤러리

갤러리 본문 영역

[정보] Continuation 모나드를 알아보자

이읗갤로그로 이동합니다. 2022.04.22 00:43:53
조회 6326 추천 22 댓글 15
														



1. Continuation Passing Style (CPS) 를 알아보자



함수형 쪽으로 좀 깊게 파고 들어가다보면 접하게 되는 개념 중에 Continuation Passing Style, 줄여서 CPS라고 하는 것이 있음.

대부분은 들어본 적도 없을거다. 왜냐하면 이건 함슬람 중에도 극한의 복음주의자들만 하는 변태짓이기 때문이다.


아니나 다를까 하스켈도 CPS 기반의 Continuation 모나드라는 걸 기본 모나드 중 하나로 지원하는데

다른 모나드는 그래도 모나드를 이해하고 보면 어디다 쓰는 건지 대충 감이 오지만 이 녀석은 애초에 CPS를 모르면 봐도 뭔 개소린지 알 수가 없다.

이번 글에서는 바로 그 CPS라고 하는 괴물을 소개해 보기로 한다.




(1) 뭐하는 개념인가?



Continuation Passing Style (CPS) 은 코딩 스타일이다. 그런데 그 스타일이 조금 희한하다.

우선 모든 함수에다가 continuation이라는 함수 타입의 추가 인수를 넣는다.

수정된 함수는 수정하기 전의 함수와 같은 계산을 하지만

결과값을 그냥 반환하는 게 아니라 그거로 continuation을 호출한 다음 continuation이 반환한 값을 받아 다시 반환하는 함수가 된다.


말로 설명하니까 조금 어렵다. 가령 pow라는 (int, int) → int 타입의 함수가 있다면 그걸 다음과 같은 함수로 바꾸라는 것임.


7cf08168f5dc3f8650bbd58b368477681b


여기서 continuation은 계산이 끝난 후 수행될 후속 작업이라고 할 수 있는데,

보통 계산 후에 CPS로 작성한 프로그램 또는 모듈이 종료될 때까지 할 일 전체가 여기에 다 보내진다.


pow_CPSpow처럼 결과값을 받아서 또 뭘 할 수 있는 함수가 아니라,

아예 후속 작업까지 다 인자로 보내서 프로그램의 최종적인 결과값까지 얻고 그냥 프로그램을 끝내버리고 싶을 때 쓰는 그런 함수인 것이다.

당연히 RCPS 로 코딩할 프로그램의 최종 결과 타입이 된다.



CPS는 모든 함수들을 이렇게 개조한 후 그것들만 가지고 프로그래밍을 하는 행위예술이라고 보면 된다.

가령 2의 10제곱이 3의 6제곱보다 작으면 true, 그렇지 않으면 false를 반환하는 프로그램을 생각해 보자. CPS를 안 쓴다면 이렇게 코딩하면 된다.


7cf08268f5dc3f8650bbd58b36827d6df1


하지만 우리는 이걸 CPS로 코딩하기를 원한다. 그러니까 우선 위 코드를 이렇게 단계적으로 계산을 수행하는 코드로 바꾸자.


7cf08368f5dc3f8650bbd58b3684726c64


첫 번째 pow함수를 호출한 뒤 결과값 v1을 가지고 해야 할 후속 작업이 무엇인지가 명확해졌다. 3번째 줄부터 main함수 끝까지 전부다.

그걸 람다로 만들어 pow_CPS의 마지막 인자로 보내면 됨.


7cf08468f5dc3f8650bbd58b3686756aef


이 변환을 계속하면 최종적으로는 다음과 같은 결과를 얻을 수 있다.


7cf08568f5dc3f8650bbd58b3688746ef5



물론 CPS함수들도 합성 가능하다. CPS 함수들을 조합해서 더 큰 CPS함수를 만드는 것도 가능함.

아래와 같이 ab제곱과 cd제곱 중 무엇이 큰지를 확인하는 CPS 함수를 먼저 만든 다음 그걸 가지고 main을 정의할수도 있다.


7cf08668f5dc3f8650bbd58b3680756ef21b


이 경우 main은 이렇게 될거다.


7cf08768f5dc3f8650bbd58b36857c6c0b


언뜻 봐서는 이런 식으로 코드를 짜면 당연히 성능이 느려질 것 같다.

그런데 실제로는 별로 느려지지 않는다고 한다. 왜냐하면 CPS로 코딩하면 모든 함수 호출이 다 꼬리재귀가 되기 때문이다.

이 꼬리재귀 때문에 오히려 성능이 올라가는 경우도 있다고 하니 참으로 오묘한 부분이다.




(2) 이거 왜함?



여기까지의 설명만 들으면 람다 중첩된 모양이 그야말로 더럽기 그지없어서

아무리 함슬람이 힙스터들이라고는 하지만 왜 이런 걸 만들어가지고 고생을 사서 하는가 하는 의문이 절로 들것임.

이 의문을 해소하려면 우선 이 CPS라는 게 근본적으로 무엇을 하기 위해 만들어진 것인지를 알아야 한다.


CPS는 기본적으로 goto같은 제어 이동 명령이 있는 저수준 언어로 씌여진 코드를

어떻게 하면 가장 기본적인 기능들만 지원하는 함수형 언어로 쉽게 번역할 수 있을까 하는 문제를 풀다가 나왔다.

대충 자바 비스무리한 언어로 씌여진 다음과 같은 코드가 있다고 생각해 보자.


7ff08168f5dc3f8650bbd58b3685756ed5


함수의 마지막 줄에서 값 반환할 때 return v; 대신 v라고만 쓸 수 있는 언어가 꽤 있는데 (러스트, 스칼라 등) 이 언어도 그런 부류다.

programgetchar()의 반환값을 ch에 저장한 뒤 toInt(ch)를 계산해 그 결과를 반환하는 함수가 된다.


이걸 다른 언어로 번역하려고 한다고 하자.

그 언어도 거의 똑같은데 다만 함수 안에서 세미콜론(;) 쓰면 죽어버리는 병을 앓고 있어서 함수 안에 변수를 정의할 수가 없다.



그냥 chgetchar()로 치환하면 되는 거 아니냐고 생각하는 사람도 있을 것 같다.


7ff08268f5dc3f8650bbd58b368276648a


문제는 이 방법은 일반적으로 적용하기 힘들다는 것이다.

여기서야 ch가 한 번만 사용되었으니 망정이지 여러 번 사용되었다면 getchar도 여러 번 부르게 되어 코드의 동작 자체가 달라지고 만다.

지금 우리 목적은 어떤 특정 코드 하나를 번역하는 게 아니다. 모든 코드를 번역할 수 있는 알고리즘을 찾는 것이다.

정답은 다음과 같이 람다를 이용하는 것이다.


viewimage.php?id=2ab4c42ef0d0&no=24b0d769e1d32ca73ceb81fa11d02831ec6fc61317e610776c2e363a75561895e064410f2f5d91b1c8915582e0a41b54f421cf2b96ba909c4ae2bc4770574883521b


보다시피 변수 정의문은 그 변수를 이용하는 뒷부분 전체를 람다로 묶어서 그 람다 호출하는 문장으로 직접적으로 치환 가능하다.

그리고 또 한 가지 방법이 있는데 바로 CPS를 이용하는 것이다.


7ff08468f5dc3f8650bbd58b3684706b95



만약 이렇게 오직 부수 작용만을 위해 평가되는 표현식이 중간에 끼어 있다면 어떻게 하면 될까?


7ff08568f5dc3f8650bbd58b36857d6ac3


간단하다. 그냥 표현식을 사용되지 않는 변수를 정의하는 변수 정의문이라고 치고 번역하면 된다.


7ff08668f5dc3f8650bbd58b3687716976


CPS로는 이렇게 된다.


7ff08768f5dc3f8650bbd58b3686776d8c



어차피 그냥 람다로도 다 할 수 있는데 왜 굳이 CPS를 쓰냐고 묻고 싶을 것이다.

사실 지금까지는 그냥 연습이었고 진짜로 번역하고 싶은 코드들은 따로 있다. 바로 중간에 return같은 점프 명령이 있는 이런 코드들이다.


7ff08868f5dc3f8650bbd58b3684746c00


4번째 줄은 실행되지 않으니 그냥 잘라내버리면 되지 않느냐 할지도 모르겠다.

그러나 그건 번역할 코드가 간단할 때나 가능한 얘기고 여기에 ifwhile섞이기 시작하면 제어가 어디로 튈지 몰라서 안된다.

되도록이면 원본의 내용을 그대로 유지한 채로 코드를 번역하고 싶은데 이 때 CPS가 활약을 한다.


7ff08968f5dc3f8650bbd58b3686706b73


여기서 return_CPS는 이렇게 정의되는 함수다.


viewimage.php?id=2ab4c42ef0d0&no=24b0d769e1d32ca73ceb81fa11d02831ec6fc61317e610776c2e363a75561895e064410f2f5d91b1c8915582e0a41b54f421cf2b96ba999c42e6ee107352488361ed


continuation_에 후속 작업으로 뭐가 들어오든 개같이 무시하고 그냥 프로그램의 최종 결과를 쏴버린다.

당연히 CPS에서는 이 함수가 return같은 역할을 하게 된다.



한 마디로 CPS는 함수와 return같은 제어 이동 명령의 차이를 없앤다. 둘다 똑같이 _CPS달린 함수로 만들 수 있다는 것임.


결과적으로 온갖 goto가득한 스파게티 코드들을 위에서부터 읽어나가면서 함수형 언어로 직역할 수 있게 됨.

실로 비범한 능력이며 바로 이것 때문에 CPS를 쓴다고 봐도 무방하다.

주로 함수형 언어로 컴파일러 같은 거 만들 때 CPS를 중간 단계로 사용하거나 한다고 함.


그외에도 분산 프로그래밍이라거나 여러가지에 이 CPS가 응용이 된다고 하는데 나도 뭔 소린지 잘 모르겠으니 넘어가도록 하겠다.




(3) 치환기 callCC


앞에서 얘기했듯이 CPS를 쓰는 건 결국 제어 이동 명령을 함수랑 똑같이 취급할 수 있기 때문임. 좀 더 구체적으로 그걸 하는 방법을 알아보고 싶다.

그러니 다음 함수의 CPS 버전을 만들어 보자.


viewimage.php?id=2ab4c42ef0d0&no=24b0d769e1d32ca73ceb81fa11d02831ec6fc61317e610776c2e363a75561895e064410f2f5d91b1c8915582e0a41b54f421cf2b96bac29f1be6e944750448831cfc


방금 전 예제와 똑같은 거 아니냐고 할 수 있는데 다르다.

이번에는 CPS 프로그램을 만드는 것이 아니고 CPS 프로그램 내부에서 사용할 func1_CPS를 만드는 것이기 때문이다.


언뜻 생각해 보면 그냥 이렇게 하면 될 것 같기도 하다.


7ef08268f5dc3f8650bbd58b3689716afe


그러나 이건 안 된다. 이렇게 할 경우 return_CPS가 호출되는 순간 func1_CPS만 끝나는 게 아니라 프로그램 전체가 끝나 버리기 때문이다.




해결책은 func1_CPS 안에서만 return_CPS의 정의를 바꾸는 것임.


7ef08368f5dc3f8650bbd58b368076698897


이제 return_CPS는 제어를 프로그램 끝으로 보내는 게 아니라 func1_CPS의 후속 작업으로 보낸다.

우리가 원하는 return_CPS의 행동 바로 그것이며 잘 작동한다.

문제는 이 방법을 쓰려면 매번 CPS함수를 만들 때마다 그 함수 안의 return_CPS를 새로 정의해야 한다는 거다. 이건 좀 많이 번거롭다.

애초에 우리가 가정한 언어는 함수 안에서 세미콜론만 보면 발작을 일으키며 죽는다.



더 좋은 방법이 있는데 바로 어떤 치환기 함수를 만들어 그걸 이용하는 것임. 그 치환기 함수는 callCC라고 불리며 다음과 같이 사용된다.


7ef08468f5dc3f8650bbd58b3680756f7e8b


return_CPS에 의존하는 람다 함수를 만들고 그 안에 코드를 작성한 후 그걸 callCC에 보내면 callCC가 알아서 진짜 return_CPS를 만들어 넣어 준다.

이 경우 꼭 return_CPS라는 이름을 이용할 필요도 없는 것은 물론이다.



callCC는 다음과 같이 정의하면 된다.


7ef08568f5dc3f8650bbd58b36897d6bff


말 그대로 필요한 return_CPS를 만들어서 첫 번째 인수로 전달된 함수에 넣어 주는 함수라는 것을 알 수 있다.





2. Continuation 모나드를 알아보자



사실 이번 글의 목표는 CPS 자체가 아니라 Continuation 모나드를 소개하는 것이었다.

모나드랑 do표기법에 대한 이야기가 많이 나오기 때문에 혹여나 아직 안 본 사람이 있다면 내가 전에 유동으로 쓴 이 글을 먼저 보고 오길 바람.


세상에 모나드 설명해준다는 놈 치고 제대로 설명해주는 놈이 없다는 건 나도 안다. 그래도 나는 나름대로 자신이 있는 편이다.

들어가서 보길 바라며 봐도 이해 안될 경우 상냥하게 욕박으면 겸허히 받아들인다.




(1) CPS 함수를 다르게 표현하기



T에서 U로 가는 f라는 함수가 하나 있다고 하자. 이 함수의 CPS버전은 타입이 이렇게 된다.


f_CPS : (T, U→R) → R


그런데 사실 f_CPS 를 꼭 이 타입으로 정의할 필요는 없음. 커링해서 아래와 같은 타입으로 만들어도 충분히 써먹을 수 있다.

아니 함수형 언어에서는 웬만해선 모든 함수를 커링하니까 오히려 이렇게 정의하는게 디폴트다.


f_CPS : T → ((U→R)→R)



보다 일반적으로는 이런 Continuation 처리기 타입이 따로 있을 수도 있을 것이다.


Cont〈R, T〉


구현은 ((T→R)→R)타입을 감싸는 래퍼 타입으로 정의할 수도 있고 그냥 ((T→R)→R)의 다른 이름으로 할 수도 있는데

이 글에서는 편의상 그냥 ((T→R)→R)의 다른 이름이라고 치겠음.

이제 f_CPS는 다음과 같은 타입의 함수가 된다.


f_CPS : T → Cont〈R, U〉


여기서 Cont, 또는 좀 더 정확하게는 Cont〈R, 〉이 바로 이번 글의 주제인 Continuation 모나드다.




(2) Continuation 모나드



이전 글에서도 설명했듯이 어떤 타입 컨스트럭터가 모나드가 되는지 알고 싶으면 liftunit, flat만 정의할 수 있는지 확인해보면 됨.

쉬우니까 코드 없이 그냥 말로 다 설명하도록 하겠다.


우선 lift를 보면 Cont〈R, 〉에 대한 lift함수는 다음과 같은 타입을 가져야 함.


lift : (T→U) → (Cont〈R, T〉→Cont〈R, U〉)


물론 여기서 Cont〈R, T〉((T→R)→R)와 같은 타입이니까 그냥 (T→U) → (((T→R)→R)→((U→R)→R)) 타입의 함수만 만들면 된다.


그리고 이것은 아주 쉽게 만들 수 있다. 왜냐하면 T에서 U로 가는 함수가 있을 경우

U→R타입의 함수에 그걸 합성하면 T→R로 가는 함수가 나오니까 U→R에서 T→R로 가는 함수도 무조건적으로 만들 수 있으며

같은 이유로 U→R에서 T→R로 가는 함수가 있을 경우 ((T→R)→R에서 ((U→R)→R)로 가는 함수도 만들 수 있기 때문이다.



unit함수는 다음과 같은 타입을 가져야 함.


unit : T → Cont〈R, T〉


당연히 T타입의 값이 있으면 (T→R)에서 R로 가는 함수를 만들 수 있다. 설명할 가치도 없는 얘기다.




flat은 어떨까? 이 함수는 다음과 같은 타입을 가진다.


flat : Cont〈R, Cont〈R, T〉〉 → Cont〈R, T〉


언뜻 보기에는 이걸 어떻게 정의하나 싶을 수 있는데 역시 Cont〈R, T〉((T→R)→R)로 치환해 놓고 보면 쉽다.


flat : ((((T→R)→R)→R)→R) → ((T→R)→R)


T→R에다가 앞에서 정의한 unit을 걸면 Cont〈R, T→R〉, 즉 ((T→R)→R)→R타입의 값이 나온다.

이걸 R로 만드는 함수가 인수로 주어져 있는데 T→R에서 R로 가는 함수를 만들지 못할 이유가 없다.


물론 Cont〈R, 〉 실제로 엄밀한 의미에서 모나드가 되려면 liftunit, flat또 무슨 조건들을 만족시켜야 하긴 하지만

이전 글에서도 그 조건 설명 안했으니 넘어가겠음. 어차피 다 된다.




(3) Continuation 모나드 활용하기



이전 글에서도 설명했듯이 모나드를 쓰는 건 그걸 통해 확장된 함수를 정의할 수 있기 때문임.

그리고 그 확장된 함수들을 항상 같은 방법으로 쉽게 합성할 수 있기 때문이다.

Continuation 모나드의 경우 확장된 함수들은 _CPS붙은 함수들인데 이것들을 쉽게 합성할 수 있다 이말이다.


앞에서 정의해 본 powCompare_CPS를 모나드를 이용해서 다시 정의해 보자.


79f08168f5dc3f8650bbd58b36897c6af4


더러운 람다 중첩이 사라지고 CPS 함수들을 일반 함수마냥 쉽게 다룰 수 있게 되었다. 왜 이걸 굳이 모나드로 만든 건지 잘 알 수 있다.




CPS 함수의 형태가 바뀌었으니 치환기 callCC도 다르게 정의되어야 한다. 새로운 callCC는 이렇게 생겼다.


79f08268f5dc3f8650bbd58b3680766add7c


앞의 callCC와 비교해 보면 인자에 continuation을 보낼 필요가 없고 함수만 보내면 되어서 사용하기가 더 편해졌다.

continuation인자가 Cont〈R, T〉로 반환 타입과 합쳐져 있기 때문에 발생하는 현상이다.




새로운 callCCdo표기법을 이용해서 func1_CPS도 다시 정의해 보자.


79f08368f5dc3f8650bbd58b36897c6c03


(하스켈의 경우 5번째 줄은 그냥 ret_CPS(0)라고만 써도 되지만 이 기능은 이전 글에서 설명하지 않았기 때문에 사용하지 않았다.)


5번째 줄의 ret_CPS(0)이 평가되는 순간 제어가 callCC블럭 밖으로 탈출한다는 것 하나만 이해하면 된다.

그거 하나만 이해해도 이걸 온갖 방법으로 창의적으로 써먹는데 아무런 문제가 없으니 굳이 실용적인 예제를 줄줄히 열거할 가치가 없다.

실용적인 예제따위 과감히 생략하고 넘어가도록 하겠다.




(4) 덤



CPS 가지고 놀면서 생각해 본 건데 callCC같은 경우 아무래도 아래로 점프하는 것밖에 못하기 때문에 한계가 있다.

위로 빽점프하는 것도 있어야 루프도 만들고 다양한 놀이를 할 수가 있다.


그럼 이게 아예 불가능한 거냐, 그건 아니고 원한다면 얼마든지 loop_CPS같은 함수도 만들 수 있다.

재귀의 원리를 응용하면 되는데 백문이 불여일견이라고 하니 그냥 아래의 예제를 보길 바람.


78f08168f5dc3f8650bbd58b3680766e45a0


여기서 loop_CPS는 함수 첫줄로 돌아가는 아래 코드의 loop점프를 대신하는 역할을 한다.


78f08268f5dc3f8650bbd58b3685716bbd


물론 진짜로 이렇게 코드를 작성했다간 무한루프 돌다가 프로그램이 뻗는다.

이건 그냥 예를 든거고 실제로는 조건문이나 루프 탈출하는 다른 점프문과 함께 사용되어야 말이 되는 프로그램이 될거다.



callCC라고 return을 위한 치환기를 만들었듯이 loop을 위한 치환기도 하나 만들어 놓으면 꽤 유용할 것 같은데

설마 이런 간단한 걸 아무도 생각 못했을 것 같지는 않고 왜 아무도 만드려 하지 않는 건지 의문임.


생각해보니 어차피 부수 효과 없는 하스켈에서는 루프 돌아봐야 아무런 쓸데가 없긴 하다.

그래도 부수 효과 있는 언어에서라면 쓸만할 거라고 생각했는데 잘 모르겠다. 아무튼 이것으로 설명을 마무리하도록 하겠음.



추천 비추천

22

고정닉 4

0

댓글 영역

전체 댓글 0
등록순정렬 기준선택
본문 보기

하단 갤러리 리스트 영역

왼쪽 컨텐츠 영역

갤러리 리스트 영역

갤러리 리스트
번호 말머리 제목 글쓴이 작성일 조회 추천
2850 설문 운전대만 잡으면 다른 사람이 될 것 같은 스타는? 운영자 24/04/15 - -
28264 공지 완장호출벨 [9] 다믜갤로그로 이동합니다. 21.12.06 38610 9
13440 공지 Github 갤러리 규칙 [5] ㅇㄹ갤로그로 이동합니다. 20.10.16 51029 13
1872 공지 Git 관련 정보 [7] 0xrgb갤로그로 이동합니다. 19.01.29 57146 11
1 공지 github에 관한 이야기를 해주세요. [5] 게으른둔재갤로그로 이동합니다. 18.08.06 49905 3
3 공지 제3자가 명백히 불쾌할 수 있는 허세 및 타당치않은 비난은 지양해주세요. [4] 게으른둔재갤로그로 이동합니다. 18.08.06 50791 10
62602 % 수학과에서 컴공 복전하는 학식인데 ㅇㅇ(112.186) 06:30 18 0
62601 % 자바 언어설계로 까는 건 좀 억까같은데 [1] ㅇㅇ(211.234) 06:26 34 0
62600 질문 과제전형 기간 꽉꽉채워서 내나요 아니면 최대한 빨리해서 내나요 [2] ㅇㅇ(218.146) 02:26 58 0
62599 질문 만 14세 미만 깃허브 학생 개발자팩 [6] 학생(1.244) 01:51 130 4
62598 % 해외 백엔드는 Go 꽤 뽑더라 [3] ㅇㅇ(223.38) 00:33 141 1
62597 % 블록체인쪽 지망하는 사람들 중에 자기가 뭘 하고 싶은건지 잘 모르는 사람 [2] 노럐갤로그로 이동합니다. 00:31 106 4
62596 % 언어별 호감도 여론조사 (4/10) - Java [2] ㅇㅇ(1.237) 00:11 130 0
62595 % 전자정부는 스프링 부트 버전이나 빨리 따라갔으면 싶음 [1] ㅇㅇ(211.226) 00:05 67 0
62594 % 왜 웹에서 네비게이터에 클립보드가 있노 [1] ㅇㅇ(118.127) 04.19 71 1
62592 % 학식들은 국내에서 좋은 기업 들어가고 싶으면 자바 스프링 하는게 나은듯 [5] ㅇㅇ(61.79) 04.19 196 6
62591 질문 sql 이런식으로 작성하면안되는거임? [5] ㅇㅇ(39.127) 04.19 195 1
62589 % 비전공 신입 영끌 6천 굿ㅋㅋㅋ [2] ㅇㅇ(116.122) 04.19 233 5
62588 % 프갤이 너무심하게 망해서 분탕은 어쩔수없다 ㅇㅇ(118.235) 04.19 94 4
62587 % 웹프론트 개발은 공부할 엄두도 안나네 어후 ㅇㅇ(122.36) 04.19 83 0
62586 % 근데 코프링할꺼면 노드든 닷넷하믄안댐? [5] ㅇㅇ(218.147) 04.19 137 2
62584 % 코딩은 중딩도 주 100시간 갈아넣으면 6천은 받음 [1] ㅇㅇ(49.169) 04.19 167 2
62583 질문 neovim lspconfig 씀? mason 씀? [3] ㅇㅇ(220.94) 04.19 70 0
62580 질문 실전형 코딩테스트 융합형 과제문제는 왜 찾기가 힘들지? [3] 백야∞갤로그로 이동합니다. 04.19 154 0
62579 % Gleam 개꼴릿하네 ㅇㅇ(211.246) 04.19 107 0
62577 질문 바이토닉 소트 이거 나만 어렵냐 [3] ㅇㅇ(139.180) 04.19 121 0
62576 정보 어떻게 언어 이름이 좆 [2] ㅇㅇ갤로그로 이동합니다. 04.19 182 1
62575 질문 스프링에서 자바, 코틀린 혼용하는 거 어케 생각함 [13] 던전마스터2갤로그로 이동합니다. 04.19 213 0
62574 % 지금 개발 고점매수니 뭐니 하면서 착각하는데 [1] ㅇㅇ(172.225) 04.19 296 11
62573 질문 wpf 유지보수하라고 받은 코드중에 이런거 많던데 [7] ㅇㅇ(39.7) 04.19 148 2
62571 % 이상한번역 [10] ㅇㅇ(59.10) 04.19 269 5
62570 질문 임베디드나 컴퓨터 그래픽스는 아무나 못하지? [12] ㅇㅇ(106.101) 04.19 275 1
62568 질문 db 유명한 책 있나요?? [1] ㅇㅇ(118.235) 04.19 154 1
62567 % 러스트 단점 [1] ㅇㅇ(118.42) 04.19 154 2
62566 % gta5 소스코드 보는데 [4] 토큰몬스터갤로그로 이동합니다. 04.19 255 0
62565 % 아주 학식들 소굴이 됐네 [2] ㅇㅇ(121.133) 04.19 260 7
62563 질문 취미로 게임 개발 해보는 중인데 질문.. [4] ㅇㅇ(39.7) 04.19 153 0
62562 질문 정보처리기사 필기 책 2021년도꺼 봐도 상관없어?? [1] ㅇㅇ(118.235) 04.19 82 0
62561 % 요즘은 서비스, 솔루션은 다 코프링 쓰냐? [5] ㅇㅇ(211.246) 04.19 191 0
62560 % 아니 코드쓸때 왜 주석을 안다는거임 [8] ㅇㅇ(223.39) 04.19 274 2
62559 % 취업 지원할 때 자신의 언어, 프레임워크 숙련도를 1~4로 [8] ㅇㅇ(125.129) 04.19 185 1
62558 질문 코테 문제는 아무리 시간이 걸려도 풀기만 하면 됨? [4] ㅇㅇ(106.101) 04.19 158 0
62557 질문 와 님들 리트코드 왤케 어려움?? [12] 한물(117.111) 04.19 263 0
62556 % 근데 개발자 대우는 원래 이랬음 [5] ㅇㅇ(221.144) 04.19 515 14
62555 질문 취업할때 커밋 메시지나 브랜치도 봄?? [9] ㅇㅇ(182.215) 04.19 272 0
62554 % 주요 계정들 다 실명인데 [7] ㅇㅇ(124.28) 04.19 312 0
62553 % 컴파일러 공부 일환으로 JSON파서부터 만들어보는데 [1] ㅇㅇ(1.240) 04.19 240 3
62552 질문 vim 구현부로 이동 기능이 없나 했는데 [19] ㅇㅇ(121.140) 04.19 279 0
62551 % 코딩 입문 언어 추천해줌 [3] 개발갤로그로 이동합니다. 04.19 251 2
62550 질문 리눅스 배워보고싶었는데 눈물나내 [13] 글쓴이(218.50) 04.19 318 1
62549 % 땔감인데 htmx 도입시킴ㅋㅋ [2] ㅇㅇ(211.234) 04.19 314 0
62548 질문 우분투 설치 오류났다 도와줘 [2] 리눅스먼지(1.234) 04.19 91 0
62546 % 어느순간부터 프레임워크가 저급레벨에서 [2] ㅇㅇ갤로그로 이동합니다. 04.19 313 0
62545 질문 C언어 스택 문제 질문좀 [4] ㅇㅇ(112.149) 04.19 148 0
62544 % elixir 찍먹 먹는도중 포기 [2] ㅇㅇ(222.113) 04.19 150 0
62543 % 자바는 코틀린 c# 발사대임 [3] ㅇㅇ(223.39) 04.18 246 2
갤러리 내부 검색
제목+내용게시물 정렬 옵션

오른쪽 컨텐츠 영역

실시간 베스트

1/8

뉴스

디시미디어

디시이슈

1/2