2012-11-04 5 views
2
이 코드 여기

마르코프 청소 기능 문제

/* 
* Markov chain random text generator. 
*/ 

#include <string.h> 
#include <stdlib.h> 
#include <stdio.h> 
#include <string.h> 
#include <time.h> 
#include "eprintf.h" 

enum { 
NPREF = 2, /* number of prefix words */ 
NHASH = 4093, /* size of state hash table array */ 
MAXGEN = 10000 /* maximum words generated */ 
}; 

typedef struct State State; 
typedef struct Suffix Suffix; 

struct State { /* prefix + suffix list */ 
char* pref[NPREF]; /* prefix words */ 
Suffix* suf;   /* list of suffixes */ 
State* next;   /* next in hash table */ 
}; 

struct Suffix { /* list of suffixes */ 
char* word;   /* suffix */ 
Suffix* next;   /* next in list of suffixes */ 
}; 

State* lookup(char *prefix[], int create); 
void build(char *prefix[], FILE*); 
void generate(int nwords); 
void add(char *prefix[], char *word); 

State* statetab[NHASH]; /* hash table of states */ 

char NONWORD[] = "\n"; /* cannot appear as real word */ 

/* markov main: markov-chain random text generation */ 
int main(void) 
{ 
int i, nwords = MAXGEN; 
char *prefix[NPREF];  /* current input prefix */ 

int c; 
long seed; 

setProgName("markov"); 
seed = time(NULL); 

srand(seed); 
for (i = 0; i < NPREF; i++) /* set up initial prefix */ 
    prefix[i] = NONWORD; 
build(prefix, stdin); 
add(prefix, NONWORD); 
generate(nwords); 
return 0; 
} 

const int MULTIPLIER = 31; /* for hash() */ 

/* hash: compute hash value for array of NPREF strings */ 
unsigned int hash(char* s[NPREF]) 
{ 
unsigned int h; 
unsigned char *p; 
int i; 

h = 0; 
for (i = 0; i < NPREF; i++) 
    for (p = (unsigned char *) s[i]; *p != '\0'; p++) 
     h = MULTIPLIER * h + *p; 
return h % NHASH; 
} 


/* lookup: search for prefix; create if requested. */ 
/* returns pointer if present or created; NULL if not. */ 
/* creation doesn't strdup so strings mustn't change later. */ 
State* lookup(char *prefix[NPREF], int create) 
{ 
int i, h; 
State *sp; 

h = hash(prefix); 
for (sp = statetab[h]; sp != NULL; sp = sp->next) { 
    for (i = 0; i < NPREF; i++) 
     if (strcmp(prefix[i], sp->pref[i]) != 0) 
      break; 
    if (i == NPREF)  /* found it */ 
     return sp; 
} 
if (create) { 
    sp = (State *) emalloc(sizeof(State)); 
    for (i = 0; i < NPREF; i++) 
     sp->pref[i] = prefix[i]; 
    sp->suf = NULL; 
    sp->next = statetab[h]; 
    statetab[h] = sp; 
} 
return sp; 
} 

/* addsuffix: add to state. suffix must not change later */ 
void addsuffix(State *sp, char *suffix) 
{ 
Suffix *suf; 

suf = (Suffix *) emalloc(sizeof(Suffix)); 
suf->word = suffix; 
suf->next = sp->suf; 
sp->suf = suf; 
} 

/* add: add word to suffix list, update prefix */ 
void add(char *prefix[NPREF], char *suffix) 
{ 
State *sp; 

sp = lookup(prefix, 1); /* create if not found */ 
addsuffix(sp, suffix); 
/* move the words down the prefix */ 
memmove(prefix, prefix+1, (NPREF-1)*sizeof(prefix[0])); 
prefix[NPREF-1] = suffix; 
} 

/* build: read input, build prefix table */ 
void build(char *prefix[NPREF], FILE *f) 
{ 
char buf[100], fmt[10]; 

/* create a format string; %s could overflow buf */ 
sprintf(fmt, "%%%ds", sizeof(buf)-1); 
while (fscanf(f, fmt, buf) != EOF) 
    add(prefix, estrdup(buf)); 
} 

/* generate: produce output, one word per line */ 
void generate(int nwords) 
{ 
State *sp; 
Suffix *suf; 
char *prefix[NPREF], *w; 
int i, nmatch; 

for (i = 0; i < NPREF; i++) /* reset initial prefix */ 
    prefix[i] = NONWORD; 

for (i = 0; i < nwords; i++) { 
    sp = lookup(prefix, 0); 
    if (sp == NULL) 
     eprintf("internal error: lookup failed"); 
    nmatch = 0; 
    for (suf = sp->suf; suf != NULL; suf = suf->next) 
     if (rand() % ++nmatch == 0) /* prob = 1/nmatch */ 
      w = suf->word; 
    if (nmatch == 0) 
     eprintf("internal error: no suffix %d %s", i, prefix[0]); 
    if (strcmp(w, NONWORD) == 0) 
     break; 
    printf("%s\n", w); 
    memmove(prefix, prefix+1, (NPREF-1)*sizeof(prefix[0])); 
    prefix[NPREF-1] = w; 
} 
} 

에 의해 생성 된 해시 테이블을 정리하는 함수를 작성하는 것을 시도하고있다

은 내가 청소 기능을 위해 지금까지 무엇을 가지고

/*Clean Function*/ 
void clean_up(State *sp) 
{ 
State *temp; 
Suffix *temp2, temp3; 

for(int h = 0; h < NHASH; h++) 
{ 
    for (sp = statetab[h]; sp != NULL; sp = sp->next) 
    { 
     while(sp->suf != NULL) 
     { 
      temp2= sp->suf; 
      temp3= *temp2->next; 
      free(temp2); 
      sp->suf= &temp3; 
     } 

    } 
} 
} 

나는 바른 길에 메신저가 있다고 생각하는데 해시 테이블의 각 색인을 통과 한 다음 상태에서 상태로 이동하고 접미사를 해제합니다. 프리픽스에 대해 어떻게해야할지 모르겠다. 각 상태를 해제하기 전에 프리픽스를 해제해야하기 때문이다. 어떤 도움이라도 대단히 감사하겠습니다.

+0

왜 당신이 함수에 인수로'sp'를 넘겨 주느냐는'statetab [h]'값을 할당 할 때 무엇입니까? 'statetab' 전체를 정리한다면,'sp'를 인자로 사용할 필요가 없습니다 (내부'for' 루프를위한 지역 변수가 될 수 있습니다). 인수로서'sp'를 사용하여 뭔가를 할 의도가 있다면, 그것을 실행하고 그것을 지역 임시 변수로 재사용하지 마십시오. (작동 할지라도, 같은 변수, 특히 인수를 사용하는 것은 명확한 코딩이 아닙니다. , 동일한 기능의 다른 부분에서 완전히 다른 두 가지 목적으로). –

+0

[프로그래밍 실습] (http://cm.bell-labs.com/cm/cs/tpop/index.html)은 훌륭한 책입니다. 그렇지 않습니다! –

답변

1

코드에서 자동 메모리 ("스택"에 있음)에있는 temp3 노드에 복사 중입니다.이 메모리에 sp-> suf를 지정하면 (루프의 다음 반복시) free가됩니다. 이 객체의 주소로 호출 (하지가 malloc에 ​​의해 수득되었으며, 이에 의해 해제 될 수없는()) 예 코드 커니 핸 파이크로 The Practice of Programming의 마르코프 과정에서 파생

void clean_up(State *sp) 
{ 
State *temp; 
Suffix *temp2, **pp; 

for(int h = 0; h < NHASH; h++) 
{ 
    for (sp = statetab[h]; sp != NULL; sp = sp->next) 
    { 
     for (pp = &sp->suf; *pp; *pp = temp2) 
     { 
      temp2 = (*pp)->next;  
      free(*pp); 
     } 

    } 
} 
} 
0

, 가장 훌륭한 책.

statetab을 정리하려고하면 기본 정리 기능에 인수가 필요하지 않습니다. 주를 statetab에서 직접 자유롭게하지 않도록주의해야하지만, statetab[i].next에서 체인 보조 상태를 해제해야합니다.

typedef struct State State; 
typedef struct Suffix Suffix; 

struct State { /* prefix + suffix list */ 
char* pref[NPREF]; /* prefix words */ 
Suffix* suf;   /* list of suffixes */ 
State* next;   /* next in hash table */ 
}; 

struct Suffix { /* list of suffixes */ 
char* word;   /* suffix */ 
Suffix* next;   /* next in list of suffixes */ 
}; 

State* statetab[NHASH]; /* hash table of states */ 

static void free_state(State *state); 
static void free_suffix(Suffix *suffix); 

static void cleanup(void) 
{ 
    for (int i = 0; i < NHASH; i++) 
     free_state(statetab[i]); 
} 

static void free_state(State *state) 
{ 
    if (state != 0) 
    { 
     for (int i = 0; i < NPREF; i++) 
      free(state->pref[i]); 
     free_suffix(state->suf); 
     if (state->next != 0) 
     { 
      free_state(state->next); 
      free(state->next); 
     } 
    } 
} 

static void free_suffix(Suffix *suffix) 
{ 
    if (suffix != 0) 
    { 
     free(suffix->word); 
     free_suffix(suffix->next); 
     free(suffix); 
    } 
} 

는 내가 xxxx 구조의 설계에 따라 free_xxxx() 코드를 설계 한 방법을 볼 수 있습니까?

주의 기록부 : 컴파일되지 않은 코드, 테스트 코드가 적습니다.


TPOP 사이트에서 코드를 추출하여 적용하려고했습니다. 위의 코드 해제 (구문 오류 수정, null 검사는 free_state()free_suffix())를 수정했지만 전체적으로 코드가 해제 될 데이터를 허용하도록 설계되지 않았습니다.

몇 가지 문제가 있습니다. 첫째, 일부 접두사는 할당되지 않습니다 (NONWORD). 접두어가 NONWORD인지 아닌지 여부를 테스트하여이를 해제하지는 않겠지 만 불쾌합니다. 그 접두어도 할당 할 수 있습니다 (NONWORD를 estrdup(NONWORD)으로 바꿉니다). 비어있는 포인터가 상태 테이블의 접두사에 숨겨져있는 어딘가 다른 곳이 있다고 생각합니다. malloc()에서 '할당되지 않은 메모리를 해제하는 것'('할당 된 메모리를 해제하는 것'과 별개라고합니다.)이라는 오류가 발생하지만이를 해결하지 못했습니다.

하지만 다른 문제로 바뀝니다. 접두어가 재사용됩니다. 즉, 시스템의 거의 모든 접두사가 하나의 접두어의 두 번째 단어로 사용되고 다음 접두어의 첫 번째 단어로 사용됩니다. 따라서 접두어를 쉽게 해제 할 수 없습니다.

메모리를 해제 할 수 있도록 디자인하면 '원자'시스템 (불변 문자열)이있어서 각 단어가 한 번 할당되고 자주 사용되도록 시스템을 설계 할 수 있습니다 필요한 경우 (D Hanson의 C Interfaces and Implementations: Techniques for Creating Reusable Code 참조). 상태 테이블을 비우는 코드는 비 단어 데이터에만 집중합니다. 완전한 원자 세트를 공개하는 코드도있을 것입니다.

클린업없이 valgrind에서 Markov 프로그램을 실행했습니다. 메모리 액세스 문제가없고 누출 된 데이터가 없습니다. 프로그램 종료시에는 여전히 액세스 할 수 있습니다. 저는 약 15,000 단어 (약 2900 개의 별개 단어)의 데이터 파일을 사용했으며 통계는 다음과 같습니다.

==9610== HEAP SUMMARY: 
==9610==  in use at exit: 695,269 bytes in 39,567 blocks 
==9610== total heap usage: 39,567 allocs, 0 frees, 695,269 bytes allocated 
==9610== 
==9610== LEAK SUMMARY: 
==9610== definitely lost: 0 bytes in 0 blocks 
==9610== indirectly lost: 0 bytes in 0 blocks 
==9610==  possibly lost: 0 bytes in 0 blocks 
==9610== still reachable: 695,269 bytes in 39,567 blocks 

그래서 재미있는 연습을 설정했습니다. 그러나 데이터를 깨끗하게 해제 할 수 있도록 메모리 할당 메커니즘을 일부 변경하지 않으면 달성 할 수 없다고 생각합니다.

은).의 I 믿습니다 (BSD에, 따라서 맥 OS X에 너무, BSD에서 setprogname()main() 전에 자동으로 호출된다. setprogname()getprogname()라고 <stdlib.h> 함수의 한 쌍있다 argv[0]와 (예정 도착 할 수 <stdlib.h>의 선언과 eprintf.h 충돌의 선언은 왜 문제의 코드는이 const char * 인수했다 따라서 <stdlib.h>에 선언을 일치하도록하는 대신 원래 setprogname()setProgName()가. 내가 eprintf.hsetprogname()를 해결하기 위해 선택 사용한다.)


TPOP는 http://plan9.bell-labs.com/cm/cs/tpophttp://cm.bell-labs.com/cm/cs/tpop에서 이전했지만 모두 지금 (2015년 8월 10일) 깨진입니다. 또한 위키 피 디아 (TPOP)를 참조하십시오.