9. 2 랭커 라이브러리 만들기

이제 랭커 라이브러리를 만듭시다.

랭커 클래스도 내부에 개체의 상태를 기억할 것이 없으므로 정적 클래스로 정의할게요.

public static class Ranker

검색 요청 메서드를 작성합시다. 검색 요청 메서드는 검색 질의를 입력 인자로 받아 검색 후에 순위를 부여하여 순위화 한 결과 목록을 반환합니다.

public static List<RankedUrl> Request(string query)

결과를 보관할 컬렉션 개체를 생성합니다.

List<RankedUrl> result = new List<RankedUrl>();

검색 질의를 형태소 분석합니다.

int total_count = 0;
List<Morpheme> list = null;
list = MorphemeParser.Parse(query);
total_count = list.Count;

검색 질의를 분석한 목록의 형태소 별로 검색 및 순위를 부여합니다.

foreach (Morpheme mo in list)
{
    Calculate(result, mo.Name);
}

결과를 점수 순으로 정렬하여 반환합니다.

result.Sort();
return result;

정렬할 수 있게 WSECore 라이브러리의 RankedUrl을 비교 가능한 클래스로 변경합니다.

public class RankedUrl:IComparable<RankedUrl>

그리고 RankedUrl 클래스에 IComparable<T> 인터페이스에 약속한 CompareTo 메서드를 구현합니다.

int IComparable<RankedUrl>.CompareTo(RankedUrl other)
{
    if (Score > other.Score)
    {
        return 1;
    }
    if (Score < other.Score)
    {
        return -1;
    }
    return 0;
}

다시 Ranker 클래스의 형태소 이름으로 검색한 후에 검색 결과를 순위화하여 결과를 반환하는 메서드를 작성합시다.

private static void Calculate(List<RankedUrl> result, string mname)

먼저 WSEForSearch 라이브러리를 참조하여 정적 클래스 EHDbmForSearch의 역 파일 정보를 얻어오는 GetInvertedFile 메서드를 호출합니다.

List<InvertedElem> inv_list = EHDbmForSearch.GetInvertedFile(mname);

만약 검색 결과의 개수가 없다면 그대로 메서드를 끝냅니다.

if (inv_list.Count == 0)
{
    return;
}

역 파일 목록에서 페이지 주소와 참조 개수 및 형태소를 포함하는 목록 개수로 순위화 한 페이지 개체를 구합니다. 그리고 다시 후처리 작업을 수행합니다. 순위화 한 페이지 개체를 구하는 부분과 후처리 작업은 별도의 메서드로 작성합시다.

string url = string.Empty;
int cnt = 0;

foreach (InvertedElem inv_elem in inv_list)
{
    url = inv_elem.Url;
    cnt = inv_elem.RefCount;
    RankedUrl rurl = MakeRankedUrl(url, cnt, inv_list.Count);
    PostCalculate(result, rurl);
}

페이지 주소와 참조 개수 및 형태소를 포함하는 목록 개수로 순위화 한 페이지 개체를 구하는 메서드를 구현합시다.

private static RankedUrl MakeRankedUrl(string url, int cnt, int dcnt)

TF 값을 구하기 위해 정적 클래스 EHDbmForSearch의 GetTotalCountInUrl 메서드를 이용하여 웹 사이트에 포함한 전체 형태소 개수를 얻어옵니다.

int mcount = EHDbmForSearch.GetTotalCountInUrl(url);

DF 값을 구하기 위해 정적 클래스 EHDbmForSearch의 GetPostedUrlCount 메서드를 이용하여 수집한 전체 페이지 개수를 얻어옵니다.

int doc_cnt = EHDbmForSearch.GetPostedUrlCount();

TF 값은 웹 사이트의 전체 형태소에서 해당 형태소의 빈도수로 계산합니다.

double tf = ((double)cnt) / mcount;

DF 값은 수집한 전체 웹 사이트 개수에서 해당 형태소를 포함하는 문서 개수로 계산합니다.

double df = ((double)dcnt) / doc_cnt;

TF 값과 DF 값을 이용하여 점수를 부여합니다.

double score = tf * Math.Log(1 / df, 2);

수집한 웹 사이트 개체를 얻어와서 부여한 점수와 함께 순위화 한 페이지 개체를 생성하여 반환합니다.

PostedUrl purl = EHDbmForSearch.GetPostedUrl(url);

return new RankedUrl(purl, score);

후처리 작업을 수행하는 메서드를 작성합시다. 여기에서는 이미 결과 목록에 포함한 순위화한 페이지 개체일 때 두 개체의 점수를 합산하는 작업을 수행합니다.

private static void PostCalculate(List<RankedUrl> result, RankedUrl rurl)

먼저 결과 목록에서 입력 인자로 받은 순위화한 개체와 같은 사이트인 항목을 검색합니다. 이 부분은 별도의 메서드로 작성합시다.

RankedUrl s_mrul = FindMyRankedUrl(result, rurl);

만약 검색 결과가 있으면 두 개체의 점수를 합산합니다. 그리고 없다면 결과 목록에 입력 인자로 받은 개체를 추가합니다. WSECore 라이브러리의 RankedUrl 형식에서는 두 개체의 점수를 합산하는 Merge 메서드가 없습니다. 이 부분은 추가해야 합니다.

if (s_mrul != null)
{
    s_mrul.Merge(rurl);
}
else
{
    result.Add(rurl);
}

WSECore 라이브러리의 RankedUrl 클래스에 두 개체의 점수를 합산하는 Merge 메서드를 추가합시다.

public bool Merge(RankedUrl rurl)
{
    if (postedurl.Url == rurl.Url)
    {
        Score += rurl.Score;
        return true;
    }
    return false;
}

다시 Ranker 클래스로 돌아와서 결과 목록에 같은 페이지 주소의 순위화 한 개체를 찾는 메서드를 구현합시다.

private static RankedUrl FindMyRankedUrl(List<RankedUrl> result, RankedUrl rurl)

결과 목록의 각 개체의 Url 정보가 입력 인자로 받은 개체의 Url 정보가 같은 개체를 찾아 반환합니다.

foreach (RankedUrl s_rurl in result)
{
    if (s_rurl.Url == rurl.Url)
    {
        return s_rurl;
    }
}
return null;

▷ Ranker.cs

using System;
using System.Collections.Generic;
using WSE_Core;
using MorphemeParserLib;
using DBMForSearchLib; 

namespace RankerLib
{
    /// <summary>
    /// 랭커 정적 클래스
    /// </summary>
    public static class Ranker
    {
        /// <summary>
        /// 검색 요청
        /// </summary>
        /// <param name="query">검색 질의</param>
        /// <returns>순위화 한 페이지 개체 목록</returns>
        public static List<RankedUrl> Request(string query)
        {
            List<RankedUrl> result = new List<RankedUrl>();
            int total_count = 0;
            List<Morpheme> list = null; 
            list = MorphemeParser.Parse(query);
            total_count = list.Count; 
            foreach (Morpheme mo in list)
            {
                Calculate(result, mo.Name);
            }
            result.Sort();
            return result; 
        } 
        private static void Calculate(List<RankedUrl> result, string mname)
        {
            List<InvertedElem> inv_list = EHDbmForSearch.GetInvertedFile(mname); 
            if (inv_list.Count == 0)
            {
                return;
            } 
            string url = string.Empty;
            int cnt = 0; 
            foreach (InvertedElem inv_elem in inv_list)
            {
                url = inv_elem.Url;
                cnt = inv_elem.RefCount; 
                RankedUrl rurl = MakeRankedUrl(url, cnt, inv_list.Count);
                PostCalculate(result, rurl);
            }
        } 
        private static void PostCalculate(List<RankedUrl> result, RankedUrl rurl)
        {
            RankedUrl s_mrul = FindMyRankedUrl(result, rurl);
            if (s_mrul != null)
            {
                s_mrul.Merge(rurl);
            }
            else
            {
                result.Add(rurl);
            }
        } 
        static RankedUrl FindMyRankedUrl(List<RankedUrl> result, RankedUrl rurl)
        {
            foreach (RankedUrl s_rurl in result)
            {
                if (s_rurl.Url == rurl.Url)
                {
                    return s_rurl;
                }
            }
            return null;
        }
 
        private static RankedUrl MakeRankedUrl(string url, int cnt, int dcnt)
        {
            int mcount = EHDbmForSearch.GetTotalCountInUrl(url);
            int doc_cnt = EHDbmForSearch.GetPostedUrlCount();
 
            double tf = ((double)cnt) / mcount;
            double df = ((double)dcnt) / doc_cnt;
            double score = tf * Math.Log(1 / df, 2);
            PostedUrl purl = EHDbmForSearch.GetPostedUrl(url);
            return new RankedUrl(purl, score);
        }
    }
}

▷ RankedUrl.cs(WSECore 라이브러리 코드 변경)

using System;
 
namespace WSE_Core
{
    /// <summary>
    /// 순위화한 페이지
    /// </summary>
    [Serializable]
    public class RankedUrl:IComparable<RankedUrl>
    {
        PostedUrl postedurl;

        /// <summary>
        /// 수집한 페이지
        /// 가져오기
        /// </summary>
        public PostedUrl PUrl
        {
            get
            {
                return postedurl;
            }
        }

        /// <summary>
        /// 사이트 주소
        /// 가져오기
        /// </summary>
        public string Url
        {
            get
            {
                if (postedurl != null)
                {
                    return postedurl.Url;
                }
                return string.Empty;
            }
        }

        /// <summary>
        /// 평가 점수
        /// 가져오기 및 설정하기
        /// </summary>
        public double Score
        {
            get;
            set;
        }

        /// <summary>
        /// 기본 생성자
        /// </summary>
        public RankedUrl()
        {
            postedurl = null;
            Score = 0;
        }
        /// <summary>
        /// 생성자
        /// </summary>
        /// <param name="purl">수집한 페이지</param>
        /// <param name="score">평가 점수</param>
        public RankedUrl(PostedUrl purl,double score)
        {
            postedurl = purl;
            Score = score;
        }
        /// <summary>
        /// 두 개의 개체의 점수 합산
        /// </summary>
        /// <param name="rurl">순위화 한 페이지 개체</param>
        /// <returns>합산 성공 여부</returns>
        public bool Merge(RankedUrl rurl)
        {
            if (postedurl.Url == rurl.Url)
            {
                Score += rurl.Score;
                return true;
            }
            return false;
        }
        /// <summary>
        /// ToString 재정의
        /// </summary>
        /// <returns>페이지 제목</returns>
        public override string ToString()
        {
            if (postedurl == null)
            {
                return string.Empty;
            }
            return postedurl.Title;
        } 
        int IComparable<RankedUrl>.CompareTo(RankedUrl other)
        {
            if (Score > other.Score)
            {
                return 1;
            }
            if (Score < other.Score)
            {
                return -1;
            }
            return 0;
        }
    }
}