Artificial Intelligence: A Modern Approach 3rd 번역서 "인공지능: 현대적 접근방식" 나왔습니다.

류광, 2016/02/11 22:24
Artificial Intelligence: A Modern Approach 3rd 번역서 "인공지능: 현대적 접근방식" 출간 및 증정 이벤트 소식.

Artificial Intelligence: A Modern Approach 3rd의 번역서가 지난 달 말에 "드디어" 출간되었습니다.

인공지능: 현대적 접근방식

페이지 수가 많아서 두 권으로 나누어 출간되었습니다. 항상 곁에 두고 공부하기에는 이 구성이 더 나을 것입니다.

어쩌다 보니 작년(2015)에는 독자 증정 이벤트가 없었는데요. 올해 첫 독자 증정 이벤트는 이곳이나 GpgStudy.com이 아니라 게임코디에서 진행했습니다(예, 이미 끝났습니다). 이벤트를 주관하신 게임코디 관리자 이주행 님과 후원해 주신 넷텐션 배현직 님께 감사 인사 드립니다.

혹시 제 블로그 방문자 분들 중 이벤트를 기다리신 분이 있다면 죄송합니다. 이 글을 좀 더 일찍 올렸어야 하는데, 설 연휴도 있었고 또 새해 인사 글에서 언급한 The Practice of Cloud System Administration의 막판 교정 때문에 여러 모로 정신이 없었습니다... .

이 인공지능 책의 독자 증정 이벤트를 게임코디에서 진행한 것은, 최근 몇 년 간 게임 개발서를 번역하지 못해서 아쉬운 마음도 있고 해서 게임 개발자들에게 책을 몇 권 드리고 싶었기 때문입니다. 애초에 제가 인공지능에 관심을 가지게 된 것이 컴퓨터 게임 때문이기도 했고요(문서 창고에서 보듯이 초창기 이곳 occam's Razor의 주된 콘텐트는 게임 개발이었고, 그 중 게임 인공지능이 한 몫을 했습니다).

다음 번 이벤트는 언제가 될지는 미정이지만, 이 블로그에서 공지할 것은 확실하니 자주 들러 주세요!

top
트랙백 0 : 의견 # + 0

2016년 새해 복 많이 받으세요!

류광, 2016/01/10 16:51

조금 늦었지만, 이 사이트와 제 번역서를 아껴 주신&주실 모든 분께 새해 인사 올립니다. 새해 복 많이 받으시고 건강하세요!

올해 첫 번역서 출간 소식은 Artificial Intelligence: Modern Approach 3rd이 될 것입니다. 이번 달에(어쩌면 이번 주에) 나올 것이고요. 그 다음은 지난 달에 탈고한 The Practice of Cloud System Administration인데, 확실하지는 않지만 상반기에 나올 것입니다. 재작년에 이어 작년에도 게임 개발서를 못해서 아쉬운데, 올해 다시 도전해 보겠습니다. 블로그 글 좀 더 많이 쓰겠다는 결심도 다시 반복이고요;;;

신년 축하 차원에서 남는 스팀 키 몇 개 뿌립니다. 아무나 하나씩만 가져가세요! (게임 등록 방법; 등록 후 간단히 댓글 남겨 주시면 다른 분들이 시간 낭비를 하지 않을 것입니다)

  • XCOM: Enemy Unknown - ?DWP2-3GZGY-XA65L? (? == 올해 천의 자리)
  • Alien Breed: Impact - 39T4D-7T?MN-DD5HM (? == 백의 자리)
  • Hero of the Kingdom - D7VLG-X2PKK-?0760 (? == 일의 자리)

(스팀 키만 긁어가는 봇들이 있다고 들어서 간단한 문제 형태로 만들었습니다. 십의 자리 숫자가 포함된 키는 가진 게 없네요...)

top
트랙백 0 : 의견 # + 0

루아 모듈의 이름 충돌 방지(1/n)

류광, 2015/12/30 22:57
이번 글에서는 순수 루아 스크립트 모듈의 다양한 형태를 나열하고, 이름 충돌 방지에 가장 좋은 형태를 제시합니다.

다음은 가장 “원시적인” 형태의 루아 모듈의 예입니다. 흔히 볼 수 있는 C 함수 라이브러리와 비슷한 '함수 모음' 형태입니다.

-- mymp3lib.lua --

function open(filename)
	-- MP3 파일 적재 --
	return handle -- 음원 자료에 대한 핸들
end

function play(handle)
	-- 해당 MP3 음원 재생 --
end

function stop(handle)
	-- 해당 MP3 음원 중지 --
end

function close(handle)
	-- 해당 MP3 음원 자료 해제 --
end

이 모듈은 이를테면 이런 식으로 사용합니다.

require"mymp3lib"

...

local music = open(filename)
play(music)

...

if some_condition(music) then
	stop(music)
end

...

close(music)

그런데 open, play, stop, close 같은 이름은 다른 라이브러리에도 많이 쓰입니다. 예를 들어 위의 mymp3lib.lua를 만든 사람과는 다른 누군가가 다음과 같은 동영상 재생 모듈을 가능성은 언제나 존재합니다.

-- movie_lib.lua --

function open(filename)
	-- 동영상 파일 적재
end

function play(handle)
	-- ...
end

function stop(handle)
	-- ...
end

function close(handle)
	-- ...
end

이 두 모듈은 함께 사용할 수 없습니다. 만일 클라이언트 스크립트에서

require"mymp3lib"
require"movie_lib"

라고 하면, 둘째 require에서 적재한 open, play 등의 함수들이 첫 require에서 적재된 해당 함수들을 덮어 쓰기 때문입니다. 아시다시피 루아에서 함수는 일급 객체입니다. 다른 말로 하면 함수는 함수 형식의 값을 가진 변수입니다. open 하나만 놓고 보면, 위의 코드는 사실상 다음과 같습니다.

-- require"mymp3lib" 에 의해
open = function(filename)
	-- MP3 파일 적재 --
	return handle -- 음원 자료에 대한 핸들
end

-- require"movie_lib" 에 의해
open = function(filename)
	-- 동영상 파일 적재
	...
end

같은 전역 변수에 두 번 값을 배정한다는 점에서 이는 본질적으로

a = 1
a = 2

와 마찬가지입니다.

이런 문제가 발생할 확률을 줄이는 한 가지 방법은 함수 이름에 접두어를 붙이는 것입니다(이를테면 movie_lib_open 등). OpenGL이나 pthreads 같은 C 함수 라이브러리가 그런 방법을 사용합니다. 루아에서는 다음처럼 함수들을 하나의 테이블로 묶는 방법도 흔히 쓰입니다.

-- mymp3lib.lua --

mymp3 = {}
function mymp3.open(filename)
	-- MP3 파일 적재 --
	return handle -- 음원 자료에 대한 핸들
end

function mymp3.play(handle)
	-- 해당 MP3 음원 재생 --
end

function mymp3.stop(handle)
	-- 해당 MP3 음원 중지 --
end

function mymp3.close(handle)
	-- 해당 MP3 음원 자료 해제 --
end
-- movie_lib.lua --

movie_lib = {}
movie_lib.open = function(filename)
	-- 동영상 파일 적재
end

movie_lib.play = function(handle)
	-- ...
end

movie_lib.stop = function(handle)
	-- ...
end

movie_lib.close = function(handle)
	-- ...
end

이전 방식이 C 함수 라이브러리와 비슷했다면 이번 것은 C++ 이름공간(namespace)을 활용하는 함수 라이브러리와 비슷합니다. 이제 클라이언트 스크립트에서 둘을 섞어 쓸 수 있습니다.

require"mymp3lib"
require"movie_lib"

local music = mymp3lib.open(arg[1])

local movie = movie_lib.open(arg[2])

...

그런데 이름공간(테이블) 이름 자체가 충돌할 수 있습니다. 예를 들어 누군가가 "movie_manager.lua"라는 이름으로 다음과 같은 모듈을 만들 수도 있습니다. 하필이면 movie_lib이라는 테이블 이름(앞의 movie_lib.lua의 것과 같은)을 사용한다는 점에 주목하세요.

-- movie_manager.lua --
movie_lib = {}

movie_lib.register = function(title, year, director, actors)

  -- 주어진 제목, 연도, 감독, 배우들로
  -- 영화 라이브러리에 영화 항목 추가

end

-- ... 기타 등등

그러면 다음과 같은 스크립트는 오류를 일으킵니다.

require"movie_lib"
require"movie_manager"

movie_lib.open(arg[1]) -- movie_lib에 open이라는 함수가 없음

두 번째 require 때문에 전역 테이블 movie_lib이 통채로 덮어 쓰였기(좀 더 장확하게는 전역 변수 movie_lib이 다른 테이블을 가리키게) 때문에, movie_lib에는 더 이상 open이라는 함수가 없습니다.

이전 방식이 이름 충돌의 확률이 높았다면, 이번 방식은 이름 충돌의 확률은 줄었지만 피해가 커졌습니다.

여담이지만, Java나 XML처럼 이름공간의 이름을 URI를 이용해서 짓는(이를테면 net.occamsrazr.movie_lib) 방식도 있습니다. 그런데 생각해 보면 이 방법은 이름 충돌을 방지하는 기법이라기보다는 이름 충돌이 발생했을 때 당사자간의 분쟁을 좀 더 확실하게 해소하기 위한 관례일 뿐입니다. 예를 들어 occamsrazr.net 도메인 소유자인 제가 아닌 누군가가 net.occamsrazr.movie_lib라는 모듈을 만들었다면 뭔가 이상한 것입니다...

한편, 루아 5.1에서는 이런 형태의 전역 테이블 방식의 모듈을 좀 더 편리하게 작성하기 위해 module()이라는 내장 함수가 도입되었는데, 이름 충돌 해소(방지가 아니라)용으로도 사용할 수 있는 기능이 있었지만 몇 가지 이유로 5.2에서 module() 자체가 폐기되었습니다. 이에 관해서는 나중에 한 번 다루기로 하고요.

결론적으로, 모듈 이름 충돌 문제에 대한 현재 최선의 해답은 다음과 같은 형태입니다.

-- movie_lib.lua --
local M = {}

M.open = function(filename)
	-- 동영상 파일 적재
end

M.play = function(handle)
	-- ...
end

M.stop = function(handle)
	-- ...
end

M.close = function(handle)
	-- ...
end

return M

한 마디로 요약하면 "함수들이 설정된 지역 테이블을 명시적으로 반환한다"가 되겠습니다. 모듈 안에서 그 어떤 전역 변수도 설정하지 않음을, 다시 말해서 전역 이름공간을 오염시키지 않음을 주목하세요.

전역 테이블을 사용하지 않으므로, 클라이언트 스크립트에서는 모듈이 돌려준 지역 테이블을 명시적으로 받아서 적절한 변수에 배정해야 합니다.

require "movie_manager" -- 전역 movie_lib을 설정
local my_movie_lib = require"movie_lib" -- 이제는 전역 movie_lib과 무관

my_movie_lib.open(arg[1])  -- OK
...

movie_manager.lua가 이전과 동일하다고 할 때, require"movie_manager"는 전역 movie_lib 테이블을 설정합니다. 이전과는 달리 require"movie_lib"는 전역 movie_lib을 설정하지 않고 그냥 지역 테이블을 돌려주므로 더 이상 충돌이 없습니다.

잠깐 부연 설명을 하자면, 개념적으로 require()는 주어진 모듈 이름에 해당하는 스크립트의 내용을 하나의 익명 함수 안에 담고 그 익명 함수를 호출합니다. 즉, local my_movie_lib = require"movie_lib" 는 개념적으로(실제로는 모듈 이름으로부터 스크립트 파일을 찾는 과정이 관여하고 호출 시 pcall을 적용하는 등등, 이보다 조금 더 복잡합니다...) 다음과 마찬가지입니다.

local my_movie_lib = (function()
	-- movie_lib.lua --
	local M = {}

	M.open = function(filename)
		-- 동영상 파일 적재
	end

	-- ... 생략 ...

	return M
end)()

GitHub 등에서 비교적 최근 만들어진 루아 모듈들을 보면 이런 ‘패턴’을 흔히 볼 수 있을 것입니다.

마지막으로, 몇 가지 변형 스타일들을 나열해 보자면, 우선 지역 변수 M을 생략하는 스타일이 있습니다.

local open = function(filename)
	-- 동영상 파일 적재
end

-- ... 생략 ...

return {
	open = open,
	-- ... 생략 ...
}

이 스타일은 다음 예처럼 ‘구현과 인터페이스의 분리’ 원칙과 잘 맞습니다.

local open_impl = function(filename)
	-- 동영상 파일 적재
end

...

return {
	open = open_impl
	-- ... 생략 ...
}

간단한 모듈은 다음처럼 return 문 하나로 끝낼 수도 있습니다.

return {
	open = function(filename)
		-- 동영상 파일 적재
	end,
	-- ... 생략 ...
}

좀 더 본격적인 예로, 다음은 앞에서 다루지 않은 ‘클래스’ 형태의 모듈에 위의 스타일을 결합한 예입니다.

-- movie_lib.lua --

local video_engine = require"my_video_engine" -- 가상의 C/C++ 확장 모듈
local meta = {}
meta.__index = meta

function meta:play()
	return video_engine.play(self.handle)
end

-- ... stop, close, is_playing, seek 등등 ...

return {
	open = function(filename)
		local inst = {}
		inst.handle = video_engine.open(filename)
		setmetatable(inst, meta)
		return inst
	end
}

클라이언트 스크립트에서는 이런 식으로 사용합니다.

local movie_player = require"movie_lib"

local movie = movie_player.open(arg[1])

if not movie:play() then
	-- 오류 처리
end

...

마지막으로, 모듈이 반드시 테이블일 필요도 없습니다. 앞의 예에서 모듈 자체의 함수는 open 밖에 없으므로, 그냥 open 자체를 돌려주는 것도 가능합니다.

-- ... 앞 부분은 이전과 동일 ...

return function(filename)
	local inst = {}
	inst.handle = video_engine.open(filename)
	setmetatable(inst, meta)
	return inst
end

클라이언트에서는 이런 식으로 사용하고요:

loca open_movie = require"movie_lib"

local movie = open_movie(arg[1])

if not movie:play() then
	-- 오류 처리
end

...


다음 글에서는 이처럼 전역 이름공간을 오염시키지 않는 모듈 패턴을 순수 루아 스크립트가 아닌 C/C++ 확장 모듈에서 구현하는 방법을 살펴보겠습니다.

top
트랙백 1 : 의견 # + 0

근황 - 2015-12-05

류광, 2015/12/05 02:24

오랜만에 근황 글 올립니다.

저번 주에 AIMA 3rd(Artificial Intelligence: A Modern Approach 3rd) 1차 교정을 마쳤습니다. 조판 난이도가 거의 TAOCP 급이라서 조판에 시간이 아주 많이 걸렸는데요. 1차 교정 사항들을 반영하는 데에도 어느 정도 시간이 걸릴 듯 합니다.

지금은 The Practice of Cloud System Administration이라는 책을 번역하고 있습니다. 거의 막바지이고 다음 주에 탈고할 예정입니다. 꽤 괜찮은, 그리고 중요한 책인데, AIMA3rd도 그랬지만 번역 시간이 넉넉치 않았던 게 좀 아쉽습니다.

벌써 12월입니다. 올해 근황 글은 이것이 마지막일 것 같습니다. 올해 마지막 블로그 글이 되지는 말아야 할텐데 말이죠....

top
트랙백 0 : 의견 # + 0

Effective Modern C++ 번역서 나왔습니다.

류광, 2015/09/22 15:11
Effective Modern C++ 번역서 나왔습니다.

본문 열기

top
TAG C++, 번역서
트랙백 0 : 의견 # + 0

◀ PREV : [1] : [2] : [3] : [4] : [5] : [6] : [7] : [8] : ... [58] : NEXT ▶