Ajax 스무고개 프로그램 최종 버전은 http://www.backstopmedia.com/examples/e4x.html 을 참조한다. 여기서는 독자가 XML과 자바스크립트 개념에 어느 정도 익숙하다고 가정한다. 필요하다면 참고자료를 읽어본다. 프로그램을 구현하고 테스트하려면 E4X를 지원하는 브라우저가 필요하다. 파이어폭스 1.5 이상이면 충분하겠다.
이 기사에서는 "집 고양이"나 "아침 시리얼"과 같은 사용자가 생각하는 사물을 유추하는 "스무고개" 프로그램을 구현한다. 사람의 생각을 판독하려면 꽤나 복잡한 프로그램이 필요하다고 여길지도 모르겠다. 실제로 http://www.20q.net 이라는 사이트는 아주 복잡하고 정교하게 훈련된 신경망 프로그램이다. 이 www.20q.net 시스템은 지난 20여년 동안 학습을 계속했으며, 예/아니오/알 수 없음/부적절함/경우에 따라/아마도/의심스러움 등 다양한 답변을 입력 받은 후 (짐작했듯이) 모든 종류의 통계적인 분석을 거친 데이터베이스를 분석하여 여러분의 마음을 읽는다.
여기서는 다소 간단한 "스무고개" 프로그램을 구현한다. 복잡한 신경망 알고리즘이 아니라 단순한 이진 검색 알고리즘을 사용할 생각이다. 이진 검색 알고리즘은 부적절한 항목을 최대한 제거해 나가면 결국 원하는 항목에 도달한다는 이론에 기반을 둔다. 사실, "스무고개"라는 놀이도 예/아니오 질문 스무 개만 던지면 세상 어떤 것도 하나로 범위가 좁혀진다는 이론에서 나왔다.
Part 1에서는 고정된 지식 데이터베이스를 기반으로 사용자에게 질문을 던져 답을 유추하는 프로그램을 구현한다. Part 2에서는 Part 1에서 구현한 프로그램에게 새로운 지식을 가르친다. 유추한 답이 틀리면 사용자에게 올바른 답이 무엇인지 그리고 그 답을 어떻게 구분하는지 묻는다. 그런 다음, 사용자가 제공한 정보를 지식 데이터베이스에 추가한 후 게임을 다시 시작한다. 마지막으로, 학습 기능을 외부 데이터베이스와 통합하여 프로그램이 습득한 지식을 다른 사용자에게도 제공한다.
말은 쉽지만 과연 브라우저에서 스무고개 게임을 어떻게 구현할까? 사람은 직관으로 다음 질문을 결정하지만 컴퓨터는 어떻게 결정할까?
생각만큼 어렵지 않다. 사실 상당히 간단하다. 이진 검색(Binary Search) 알고리즘, 좀더 일반적으로는 분할과 정복(Divide and Conquer) 알고리즘을 사용하면 질문을 던질 때마다 정답 후보 중 절반 정도가 떨어져 나간다. 예를 들어, 정답 후보 1024개에서 시작한다고 가정하자. 첫 번째 질문을 던지고 나면 512개가 남는다. 두 번째 질문을 던지고 나면 128개, 세 번째 질문을 던지고 나면 64개가 남는다. 1024개나 되는 정답 후보도 질문 10개만 던지면 하나로 좁혀진다. 실제로 질문 20개를 던지면 104만 8576개나 되는 정답 후보를 처리할 수 있다.
알고리즘은 전혀 복잡하지 않다. 전체적인 흐름은 다음과 같다.
- 지식 데이터베이스에서 모든 질문과 정답 후보를 읽어들인다.
-
사용자에게 생각하는 답이 "동물입니까? 식물입니까? 광물입니까?"인지 묻는다.
이 질문은 전형적인 "첫 번째" 질문으로, 정답 범위를 즉석에서 1/3로 좁힌다. 이후 단계부터는 "예, 아니오"형 질문만 던진다. - 사용자 응답에 맞지 않는 정답 후보를 모두 제거한다.
-
정답 후보가 하나만 남으면 사용자에게 맞는지 확인한다.
맞다면 지식 데이터베이스를 다시 읽어들이고 게임을 새로 시작한다. 틀렸다면 사용자에게 옳은 답이 무엇인지 그리고 틀린 답과 옳은 답을 구분하는 질문이 무엇인지 묻는다. 사용자가 입력한 정보를 지식 데이터베이스에 추가한다. (이 단계는 Part 2에서 구현한다.) - 정답 후보가 여러 개 남았다면 가장 많은 항목에 적용되는 질문을 찾는다. (이 부분이 중요하다. 가장 많은 항목에 적용되는 질문을 던지면 가장 많은 항목을 제거할 가능성이 생긴다.)
- 찾은 질문을 던진다.
- 3단계로 돌아간다.
짐작하겠지만 E4X는 이런 알고리즘에 아주 적합하다.
참고: 사람들이 지식 데이터베이스에 올바른 정보만 입력하도록 강제할 방법은 없다. 하지만 올바른 정보를 입력하지 않으면 자기가 입력한 항목은 미아가 된다.
프로그래밍 환경에서 XML을 사용한다는 소리를 들으면 가장 먼저 어떤 생각이 떠오르는가? DOM? XPath? 아니면 "으악, 진짜 골치거리야!"라는 탄식? 맞다. XML은 확실히 자료를 저장하기에 멋진 형식이지만, XML 자료를 조작하는 일은 상당히 번거롭고 까다롭다. 그런데 만약 XML 개체를 쉽게 생성하고, XML 노드에 쉽게 접근하고, 불필요한 XML 노드를 쉽게 걸러내고, 화면이나 파일로 자료를 쉽게 직렬화할 수 있다면? 진짜 편하지 않을까?
짜잔, 여러분에게 E4X를 소개한다.
E4X는 자료 바인딩 방식의 구조체를 사용하여 XML 문서 일부를 쉽게 참조하게 해준다. 예를 들어 Listing 1 코드를 살펴보자.
Listing 1. E4X 사용
<script type="text/javascript;e4x=1">
myquestion = <question>
<display>Is it animal, vegetable, or mineral?</display>
<answerOption>Animal</answerOption>
<answerOption>Vegetable</answerOption>
<answerOption>Mineral</answerOption>
</question>;
alert("The question is '" + myquestion.display + "'");
</script>
|
참고: 스크립트 유형 정의를 눈여겨 살핀다.
";e4x=1"
은 초기 모질라를 위한 E4X 구현에서 일부 하위 호환 기능을 끄기 위해 필요하다.
우선, 선언한 XML 앞뒤에 인용 부호가 없다. 실수가 아니다. 진짜로 코드에다 XML을 그대로 넣어도 괜찮다. 나중에 보겠지만 여러모로 편리한 특성이다. (역시 나중에 보겠지만 문자열에서 객체를 생성하는 방법도 있다.) 둘째, myquestion을 마치 객체처럼 취급해서 display 요소 값을 가져온다. 결과는 그림 1과 같다.
그림 1. XML에서 정보를 추출해 표시하기
요소 값만이 아니다. XML을 그대로 가져오기도 아주 편리하다. Listing 2를 보자.
Listing 2. XML 그대로 가져오기
...
</question>;
alert("The question is \n" + myquestion);
|
여기서는 XML 문서 전체를 가리킨다. 처리 결과는 그림 2와 같다.
그림 2. XML 문서 전체 표시하기
쉽지 않은가? 불가사의한 직렬기(serializer)나 변환이 필요 없다. 그저 여느 객체와 같은 방법으로 자료를 참조하면 된다.
E4X는 두 가지 기본 클래스를 제공한다. 하나는
XML()
클래스로 문서나 요소를 표현한다. 다른 하나는
XMLList()
클래스로 노드 목록을 표현한다. 이 기사에서는 두 클래스를 모두 사용한다.
이제 프로그램을 구현해 보자.
첫 번째 단계로, 프로그램에서 사용할 지식 데이터베이스를 생성한다. 여기서는 XML 문서 하나를 사용하는데, 이 문서는 프로그램이 던질 질문 목록과 프로그램이 참조할 정답 후보 목록을 포함한다. 구체적인 내용은 Listing 3과 같다.
Listing 3. 지식 데이터베이스 생성
kdata =
<knowledgebase>
<questions>
<question id="1">
<display>Is it animal, vegetable, or mineral?</display>
<answerOption>Animal</answerOption>
<answerOption>Vegetable</answerOption>
<answerOption>Mineral</answerOption>
</question>
<question id="2">
<display>Does it bark?</display>
<answerOption>Yes</answerOption>
<answerOption>No</answerOption>
</question>
</questions>
<targets>
<target id="1">
<display>a house cat</display>
<answer questionid="1"><answerValue1>Animal</
answerValue1></answer>
<answer questionid="2"><answerValue2>No</
answerValue2></answer>
</target>
<target id="4">
<display>a dog</display>
<answer questionid="1"><answerValue1>Animal</
answerValue1></answer>
<answer questionid="2"><answerValue2>Yes</
answerValue2></answer>
</target>
<target id="2">
<display>a carrot</display>
<answer questionid="1"><answerValue1>Vegetable</
answerValue1></answer>
</target>
<target id="3">
<display>a ruby</display>
<answer questionid="1"><answerValue1>Mineral</
answerValue1></answer>
</target>
</targets>
</knowledgebase>;
knowledgeBase = new XML(kdata);
|
진행에 앞서 XML 문서 구조를 좀더 자세히 살펴보자. 알고리즘의 핵심이므로 이해하고 넘어가는 편이 좋다. 먼저, 문서는 크게 질문 목록(questions)과 정답 후보 목록(targets)으로 나뉜다. 질문 부분은 간단하다. 각 question 요소가 질문 하나와 가능한 답변 여럿을 포함한다. 첫 번째 질문을 제외한 나머지 질문은 모두 가능한 답변이 "yes"와 "no"다. 하지만 XML 구조 자체가 확장성이 있으므로 필요하다면 나중에 가능한 답변을 추가해도 괜찮다.
각 target 요소는 식별자(id), 출력 이름, 각 질문에서 시스템이 알고 있는 정답 후보가 해당하는 답변을 포함한다. 예를 들어, 개는 동물이고(question id=1에 대한 답변) 짖는다(question id=2에 대한 답변). 반면, 당근은 식물이다(question id =1에 대한 답변). 당근이 짖을 염려는 없으므로 question id=2에 대한 답변은 생략한다.
게임을 진행하면서 프로그램은 새롭게 배우는 질문과 답을 기존 목록에 적절히 추가한다.
참고: 처음부터 좀더 똑똑한 프로그램으로 시작하려면 http://backstop.nicholaschase.com/knowledgebase.php?getkb=YES에서 최신 지식 데이터베이스를 내려받아 사용한다.
다음 단계로, 사용자에게 첫 번째 질문을 던진다. 그러려면 질문을 브라우저에 표시해야 한다. Listing 4와 같이 사용자 정의 가능한 HTML을 생성하면 편리하다.
Listing 4. 질문 폼
<html>
<head>
<title>E4X mindreader</title>
<script type="text/javascript; e4x=1" src="e4x.js"></script>
<style type="text/css">
.answerLink {color: blue; text-decoration: underline}
</style>
</head>
<body style="background-color:#abdfe7;" onload="ask_question()">
<div id="questionFormDiv"
style="position: absolute;top: 50px;visibility: hidden; width: 100%;">
<span id="displayQuestion"></span><br />
</div>
</body>
</html>
|
위 코드에서 div는 껐다 켰다 할 수 있다. 처음에는 꺼져 있다. div 요소 안에는 텍스트를 삽입할 span 요소가 들어 있다. 이 span 요소 안에다 원하는 문자열을 삽입하여 표시한다. 그러려면 XML 문서에서 원하는 XML 요소를 선택할 수 있어야 한다.
E4X에서는 필터를 사용하여 노드나 노드 목록을 선택할 수 있다. Listing 5를 참조한다.
Listing 5. 필터 사용
...
knowledgeBase = new XML(kdata);
var currentQuestion = 1;
//******************************
// BEGIN FUNCTIONS HERE
//******************************
function ask_question(){
var questionElement = knowledgeBase.questions.question.(@id == currentQuestion)
var questionDisplay = questionElement.display;
document.getElementById("displayQuestion").innerHTML = questionDisplay ;
show_form("questionFormDiv");
}
function hide_form(divName){
document.getElementById(divName).style.visibility = "hidden" ;
}
function show_form(divName){
document.getElementById(divName).style.visibility = "visible" ;
}
|
브라우저가 HTML 페이지를 열면 프로그램은 ask_question() 함수를 호출해 첫 번째 질문을
던진다.
answer_question()
함수는 가장 먼저 questionElement라는 변수를 생성하는데, 이 변수는 첫 번째 질문을
표현하는 XML을 저장한다. (프로그램 전체에서 현재 질문 id를 알아야 하므로
currentQuestion을 전역 변수로 선언한다. 바람직한 프로그래밍 관례는 아니지만 예제
프로그램이라 편의를 우선했다.)
ask_question
함수 시작 부분을 눈여겨 살핀다. XPath 서술부(predicates)와 흡사하며 비슷한 방식으로
동작한다는 사실을 깨달으리라. 여기서는 지식 데이터베이스를 선택한 후, 루트 요소 내
questions 요소로 이동한 다음, questions 요소 내 모든 question 요소로
이동한다. 그런 다음, 필터에 만족하지 않는 요소를 걸러낸다. 위 코드에서는 id가 지정한 값이 아닌
요소를 걸러낸다.
결과적으로 id가 지정한 값과 일치하는 요소 하나만 남는다. 이 요소에서 display 자식 요소를 찾은 후 표준 DOM 함수를 사용하여 요소 값을 페이지에 표시한다. 결과는 그림 3과 같다.
그림 3. 질문 표시
이제 가능한 답변을 추가할 차례다.
페이지에서 사용자로부터 입력을 받는 방법은 다양하다. 여기서는 Listing 6과 같이 span을 사용해 링크를 흉내낸다.
Listing 6. 가능한 답변
<span id="displayQuestion"></span><br />
<span class="answerLink"
onclick="answer_question(this)" id="answer1Text"></span>
<span class="answerLink"
onclick="answer_question(this)" id="answer2Text"></span>
<span class="answerLink"
onclick="answer_question(this)" id="answer3Text"></span>
|
위 링크는 모두 (잠시 후 살펴볼)
answer_question()
함수를 호출한다. 우선 페이지에 답변부터 표시하자. Listing 7을 참조한다.
Listing 7. 답변 채우기
...
document.getElementById("displayQuestion").innerHTML = questionDisplay ;
var answerOptions = new XMLList();
answerOptions = questionElement.answerOption;
var answerCounter = 0;
document.getElementById("answer3Text").innerHTML = "";
for each( var answerText in answerOptions) {
answerCounter++;
document.getElementById("answer"+answerCounter+"Text").innerHTML =
answerText;
}
}
...
|
answerOptions
변수는 XMLList로 DOM Nodelist와 비슷하다. 여기서는 현재
questionElement
내에 있는 모든
answerOptions
요소를 포함한다. 모든
answerOptions
노드를 찾은 후에는 루프를 돌면서 각
answerOptions
노드를 차례로 참조하여 해당 span 요소에 추가한다. (참고로, 루프를 돌기 전에 세 번째 span
요소 값을 지운다. 세 번째 span 요소는 첫 번째 질문에서만 사용하기 때문이다.)
결과는 그림 4와 같다. 각 asnwerOption 요소가 링크로 표시된다.
그림 4. 첫 번째 질문
이제 사용자가 질문에 답하면 어떻게 될까?
사용자가 질문에 답하면 정답 후보 중 답변에 맞지 않는 항목을 모두 걸러내야 한다. 그러려면 Listing 8처럼 먼저 현재 고려 중인 정답 후보를 모두 가져온다.
Listing 8. 현재 고려 중인 정답 후보 가져오기
...
var currentQuestion = 1;
var currentTargetSet = knowledgeBase.targets;
//******************************
// BEGIN FUNCTIONS HERE
//******************************
function ask_question(){
...
}
function answer_question(answerSpan){
hide_form("questionFormDiv");
var theAnswer = answerSpan.innerHTML;
var remainingTargets =
currentTargetSet..target.(answer["answerValue"+currentQuestion]==theAnswer);
alert("There are "+remainingTargets.length()+" targets remaining: \n" +
remainingTargets);
}
...
|
코드 상단에 있는
currentTargetSet
이라는 새롭게 만들어진 객체를 주목한다. 이 객체는 초기 값으로 target 요소 전체를 포함한다.
그러나
answer_question()
함수가 호출될 때마다
currentTargetSet
이 포함하는 요소 수는 점차 줄어든다.
answer_question() 함수는 인수 answerSpan에서 사용자 답변을 추출한 후
currentTargetSet
을 걸러내 새로운 XMLList인
remainingTargets
를 만든다. 참고로, 인수
answerSpan
은 span 요소 전체이므로 사용자 답변을 별도로 추출해야 한다.
currentTargetSet 다음에 오는 마침표 두 개(..)에 주목한다. XPath에서 슬래시 두 개(//)를 사용하듯이, E4X는 마침표 두 개(..)를 사용하여 자식 노드로 이동한다. 여기서는 루트 요소 내 모든 target 자식 요소를 찾은 후 필터로 걸러낸다.
이제 필터를 눈여겨 살핀다. []을 보고 Xpath 서술부를 떠올릴지도 모르지만, 실상은 answer
변수가 해시(hash)라서 []을 사용한다. 자바스크립트에서 폼 요소를 참조하는 방식과 비슷하게,
answer["answerValue1"]
은
answer.answerValue1
과 동일하다.
[]을 사용하는 이유는 찾고자 하는 요소 이름을 동적으로 지정하기 위해서다. 이 경우 필터는
answer.answerValue1
이라는 자식 요소가 존재하며 그 자식 요소 값이 사용자 답변과 똑같은 요소를 찾는다.
앞서와 마찬가지로 XMLList에 필터를 적용한 결과 역시 XMLList다. 따라서 remainingTargets 길이는 남은 정답 후보 수를 뜻한다. 예를 들어, 사용자가 "Animal"을 선택하면 정답 후보는 2개가 남는다. 그림 5를 참조한다.
그림 5. 남은 정답 후보 2개
남은 정답 후보를 바탕으로 다음 질문을 결정한다. 이 때는 둘 사이를 구분짓는 질문을 던져야 한다.
알고리즘에 따르면, 다음 질문은 남은 정답 후보에 가장 많이 포함된 질문이어야 한다. 그러려면 먼저 가정이 필요하다. 실제 환경이라면 이미 던진 질문을 저장하거나 남은 정답 후보에서 이미 던진 질문은 제거해야 하는 등 좀더 일반적인 제외 방법을 고안하겠지만, 여기서는 시간이 지날수록 질문이 구체적이 되며 지식 데이터베이스가 커진다고 가정한다. 즉 시간이 지날수록 질문 id 값이 커진다는 뜻이다. 따라서 Listing 9와 같은 방법으로 이미 던진 질문을 제거할 수 있다.
Listing 9. 이미 던진 질문 제거하기
...
var remainingTargets =
currentTargetSet..target.(answer["answerValue"+currentQuestion] == theAnswer);
alert("There are "+remainingTargets.length()+" targets remaining: \n" +
remainingTargets);
var targetContainer = <targets/>;
currentTargetSet = new XML(targetContainer);
currentTargetSet.targets = remainingTargets;
if (remainingTargets.length() == 1){
// Make a guess
} else {
var remainingAnswers = currentTargetSet..answer.(@questionid > currentQuestion);
mostPopularQuestionCount = 0;
mostPopularQuestionId = 0;
get_most_popular_question(remainingAnswers);
currentQuestion = mostPopularQuestionId;
ask_question();
}
}
var mostPopularQuestionId = 0;
var mostPopularQuestionCount = 0;
function get_most_popular_question(answersToCheck){
var answersToCheckElement = <answersToCheckRoot/>;
var checkElement = new XML(answersToCheckElement);
checkElement.appendChild(answersToCheck);
var firstId = checkElement..answer[0].@questionid;
var thisQuestionCount = checkElement.answer.(@questionid == firstId).length();
if (thisQuestionCount > mostPopularQuestionCount){
mostPopularQuestionCount = thisQuestionCount;
mostPopularQuestionId = firstId;
}
answersToCheck = checkElement..answer.(@questionid != firstId);
if (answersToCheck.length() > 0){
get_most_popular_question(answersToCheck);
} else {
// alert("Most popular question is "+mostPopularQuestionId+",
with "+mostPopularQuestionCount+" answers.")
}
}
...
|
사용자 답변에 맞지 않는 정답 후보를 모두 제거한 후 하나만 남았다면 정답을 추측한다. 이 단계는 다음 절에서 설명한다.
아직도 여러 후보가 남았다면
currentTargetSet
을 다시 만든다.
remainingTargets
는 target 요소 목록일 뿐이므로, 먼저 새 XML 문서를 생성한 후 루트 아래 target 요소
목록을 추가한다.
그런 다음, 질문 id가 현재 질문 id보다 큰 남아있는 answer 요소를 포함하는 XMLList를 얻는다. 이 목록에서 가장 빈번한 질문을 다음 질문으로 선택한다.
위 코드에서 보듯이, 재귀 함수
get_most_popular_question()
은 질문 빈도수를 세어 가장 빈번한 질문을 찾는다. 함수는 인수에서 첫 번째 질문 id를 선택하여
빈도수를 센다. 그런 다음, 가장 빈번한 질문인지 그리고 다른 질문이 남았는지 확인한다. 지금까지
가장 많은 질문이 무엇인지 점검하며, 다른 질문이 남았다면, 이미 확인한 질문을 제외한 후 다시
자신을 호출한다.
마지막으로,
currentQuestion
을 가장 빈번한 질문으로 설정한 후 되돌아가 사용자에게 질문을 던진다.
우리 예제에서는 사용자가 "Animal"을 선택하면 남아있는 정답 후보로 "a house cat"과 "a dog"이 남는다. 질문 1을 제외하면, 가장 빈번한 질문은 질문 2 "Does it bark?"가 된다.
사용자가 "Does it bark?"라는 질문에 답하고 나면 후보 항목은 하나가 남는다. 사용자가 무엇이라 답하든 이제 정답을 추측할 시간이다. Listing 10을 참조한다.
Listing 10. 정답 추측하기
...
var targetContainer = <targets/>;
currentTargetSet = new XML(targetContainer);
currentTargetSet.targets = remainingTargets;
if (remainingTargets.length() == 1){
currentGuessText = currentTargetSet.target.display;
currentGuessId = currentTargetSet.target.@id;
guess();
} else {
var remainingAnswers =
currentTargetSet..answer.(@questionid > currentQuestion);
get_most_popular_question(remainingAnswers);
currentQuestion = mostPopularQuestionId;
ask_question();
}
}
var currentGuessText = "";
var currentGuessId = "";
function guess(){
document.getElementById("guessSpan").innerHTML = currentGuessText;
show_form("guessDiv");
}
...
|
추측하려는 정답은
currentTargetSet
변수에 들어 있으므로, 우선
currentGuessText
와
currentGuessId
라는 전역 변수를 생성하여 추측하려는 값과 ID를 저장한다. 그런 다음, guessDiv라는 div
요소에 값을 표시한다. Listing 11을 참조한다.
Listing 11. 추측 폼
<div id="guessDiv" style="position: absolute; top: 50px;visibility: hidden;
width: 100%;">
It's <span id="guessSpan"></span>, right?<br />
<a href="#" onclick="start_over()">YES! You're right!
Let me try again.</a><br />
<a href="#" onclick="get_new_target()">No, sorry!</a><br />
</div>
</body>
</html>
|
사용자가 "Animal", "Yes"를 선택하면 프로그램은 "a dog"이라고 추측한다. 화면은 그림 6과 같다.
그림 6. 첫 번째 추측
추측이 틀렸을 때 대응하는 방법은 Part 2에서 다룬다. 여기서는 추측이 옳다고 가정하고 다음으로 취할 단계를 설명한다.
Listing 12는
start_over()
함수다. 보다시피 아주 간단하다.
Listing 12. 다시 시작
function start_over(){
hide_form("guessDiv");
currentQuestion = 1;
currentTargetSet = knowledgeBase.targets;
ask_question();
}
|
다시 시작하려면 추측 값을 출력한 div를 숨긴다. 그런 다음
currentQuestion
과
currentTargetSet
을 재설정하고,
ask_question()
을 호출하여 첫 번째 질문을 다시 표시한다.
하지만 추측 값이 틀렸다면? 이 때는 사용자가 올바른 값을 프로그램에게 가르쳐 주어야 한다. 구체적인 방법은 Part 2에서 다룬다.
이 기사에서는 스무고개라는 게임 형식으로 E4X를 사용하여 버블 정렬(Bubble Sort) 루틴을
구현했다. 이 과정에서 E4X
XML()
객체와
XMLList()
객체를 생성하고 조작하는 방법을 익혔으며, 객체 내 데이터를 참조하는 방법도 배웠다. 또한 필터를
사용하여 불필요한 노드를 제거하는 방법도 살펴보았다.
하지만 우리 예제 프로그램은 아직도 상당히 단순하다. 실제 환경에서라면, 가능한 답변으로 “경우에 따라” 혹은 “알 수 없음” 등이 추가로 필요할지도 모른다. 하지만 가능한 답변 수가 늘어날수록 알고리즘도 그만큼 복잡해진다. 그래서 이 기사에서는 가능한 답변 수와 알고리즘 복잡도를 어느 정도 수준으로 제한했다. (좀더 복잡한 알고리즘이 필요하다면 참고자료 에 나오는 20Q.net 알고리즘 사용 권리를 획득해도 된다.) Part 2에서는 우리 프로그램에 사람들이 가지고 노는 동안 수행하는 학습 기능을 추가하고 데이터베이스와 연동해 보겠다.
| 설명 | 이름 | 크기 | 다운로드 방식 |
|---|---|---|---|
| Part 1 예제 코드 | x-e4xpart1code.zip | 3KB | HTTP |
교육
-
응용 프로그램 데모
: 이 기사에서 구현하는 스무고개 응용 프로그램이다. 완성된 응용 프로그램을 직접 사용해본다.
-
E4X 명세
(PDF 파일, 1.81MB): 공식적인 E4X(ECMAScript for XML) 문서를 읽어본다.
E4X는 ECMAScript에 원시 자료 유형으로 XML 지원을 추가한 프로그래밍 언어 확장이다.
-
Ajax and scripting Web Services with E4X
(Paul Fremantle and Anthony Elder, developerWorks, 2005년
4월): 이 기사에서는 E4X와 자바스크립트를 좀더 복잡하게 사용해서 웹 서비스 요청을 만든다.
-
예제 지식 데이터베이스
: 스무고개 응용 프로그램을 위한 최신 지식 데이터베이스를 내려 받을 수 있다.
-
20q.net
: 신경망을 사용하여 진짜 스무고개 응용 프로그램을 시도해 본다.
-
스무고개 게임
: 인간과 스무고개 게임을 진행하는 전략을 소개한다.
-
이진 검색 알고리즘
: 위키 백과에 나온 내용으로 정렬된 목록에서 특정 값을 찾아내는 알고리즘이다.
-
튜토리얼:
Build apps using Asynchronous JavaScript with XML
(Naveen Balani and Rajeev Hathi, developerWorks, 2005년
11월): Ajax(Asynchronous JavaScript with XML)를 사용하여 웹 응용
프로그램을 설계하고 개발하는 방법을 소개한다.
-
Ajax와 XML: 다섯 가지 Ajax 안티 패턴(anti-pattern) (한글)
(Jack Herrington, 한국 developerWorks, 2007년 5월): Ajax를 사용할 때
피해야 할 구현 관례를 소개한다.
-
Ajax와 XML: 다섯 개의 일반적인 Ajax 패턴 (한글)
(Jack Herrington, 한국 developerWorks, 2007년 4월): 흔히 사용하며 작업에
유용한 Ajax 설계 패턴 다섯 가지를 소개한다.
-
자바 개발자를 위한 Ajax: 동적 자바 애플리케이션 구현 (한글)
(Philip McArthy, developerWorks, 2005년 9월): Ajax를 사용하여
동적 웹 응용 프로그램을 생성하는 혁신적인 방법을 소개한다.
-
IBM XML 인증 사이트
: XML과 관련 기술 분야에서 IBM 인증 개발자가 되는 방법을 안내한다.
-
XML 기술 라이브러리
: 기술 자료, 정보, 튜토리얼, 표준, IBM 레드북 등 다양한 자료를 제공한다.
-
developerWorks 기술 행사와 웹 캐스트
: 기술 동향 세션을 놓치지 말기 바란다.
-
온라인 기술 서점
: 다양한 기술 관련 서적을 제공한다.
제품 및 기술 얻기
-
IBM 평가판 소프트웨어
: developerWorks에서 직접 내려 받아 다음번 프로젝트에 활용하자.
토론
- 포럼에 참여하기.
-
XML 포럼
: XML과 관련하여 다앙햔 토론을 나눌 수 있다.
-
developerWorks XML 영역: 생각을 나누자:
항상 여러분의 의견을 환영한다. 기사를 읽은 후 의견을 보내주기 바란다. 중재는 XML 영역
편집자들이 맡는다.
-
developerWorks 블로그
: 블로그를 살펴보고
developerWorks 공동체
에 참여하자.
Nicholas Chase는 루슨트 테크놀로지즈, 썬 마이크로시스템즈, 오라클, 템파 베이 버커니어스(프로 미식축구 팀)과 같은 회사의 웹 사이트 개발에 참여했다. 그는 또한 고등학교 물리 교사, 저준위 핵 폐기물 시설 관리자, 온라인 SF 잡지 편집자, 멀티미디어 엔지니어, 오라클 강사, 어느 통신 회사의 CTO이기도 하다. XML Primer Plus(Sams)를 비롯하여 여러 책을 집필했으며, 세컨드 라이프라는 가상 현실 컨텐츠와 응용 프로그램을 전문적으로 개발하는 InterSection Unlimited 사 파트너다. 세컨드 라이프에서 Chase Marellan으로 Chase를 찾아보기 바란다.