25 Years of Programming
An open source source for C, C++, OWL, BASIC, MDB, XLS, DOT, and more...
Home   Projects   Up   Sitemap   Search   Blog   Forum+Chat   About Us   Privacy   Terms of Use   Feedback   FAQ   Images   Services   Ads   Donate   Humor

DIC.cpp for the WTalk chatbot

Defines the word token class for the WTalk.cpp natural language processing chatbot project.

The TOK class interacts with the Microsoft Access database WTalk.mdb using SQL via Windows Dynamic Data Exchange (DDE), retrieving its initial data from it when it is constructed, and at various times updating or inserting data into the database.

dic.h

/*	dic.h	        	9-5-01
	This is part of the WTalk project.
	Copyright (C)1993-2001 Steven Whitney.
	Published under GNU GPL (General Public License) Version 2, with ABSOLUTELY NO WARRANTY.
	Initially published by http://25yearsofprogramming.com.

Dictionary classes.

------
To do:

*/
#ifndef __DIC_H
#define __DIC_H

#include "c:\bcs\library\ddemlapp.h"
#include <classlib\arrays.h>
#include "c:\bcs\my.h"
#pragma hdrstop

//////////////////////////////////////////////////////////////////////////////
// unsure where this enum will eventually go.  its values must match those in
// MDB, so they should be linked somehow.
//
// parts of speech - do not use zero or negative
// parts that user might enter must start at 1 and then be in sequence with no gaps.
// tried START = 0, but caused Illegal Word type in statmach.cpp.
// check whether the "nonzero" test in statmach is actually meaningful and needed.
enum
{
	// all .DIC file entries use absolute numeric types: do NOT change the defs here!
	UNASSIGNED = 0, // 0	also indicates illegal type, and delimits the list
	NOUN,			// 1  	including pronouns
	VERB,   		// 2
	ADJ,        	// 3  	including possessives that can follow "the"
	ADV,        	// 4
	DET,     		// 5  	articles, and possessives that can't follow "the"
	PREP,       	// 6
	CCONJ,  		// 7  	recheck word assignments to CONJ categories.
	SCONJ,  		// 8  	and I think state machine data files need revisions.
	EXCL,    		// 9  	exclamations and interjections
	PUNCT,   		// 10 	all non-TERM punctuation except
	TERM,    		// 11 	terminator: (.?;!:)  not sure how to handle punct.
	VBHELP,  		// 12 	helper word in multi-word verb tenses: example: to
	NADV			// 13	Adverb that cannot modify a verb by itself, and usually
					// 		cannot exist by itself; must modify an ADV or ADJ
};

/////////////////////////////////////////////////////////////////////////////
// A TOK is one word or punctuation mark in a sentence, with info on its usage.
// A number of its variables (e.g. TypeList) are maintained locally because a DDE lookup
// for every use is much too slow.  However, these local variables only apply to THIS tok;
// any subsequent toks created get the latest info from MDB.
//
class TOK
{
public:
	// "" prevents lookup time, "word" ensures all have length for debugging
	TOK(string s = "");
	TOK(const TOK& other);  									// copy

	// comparison functions
	BOOL operator == (const TOK& other) const;	// exact match spelling and typenow (usage)
	double Similarity(const TOK& other);   		// how similar they are, by various measures
	double Similarity(const TOK* other) { return Similarity(*other); }

	// haven't decided whether these should just output the strings, or what
	friend ostream& operator << (ostream& os, const TOK&);  	// write

	// functions
	BOOL AnyUndefinedWords();			// whether any in MDB: fn not currently used
										// could be static fn.
	long LookupAlphaID();				// look up in MDB
	uint LoadTypes();
	int TypeCount() { return TypeList.length(); }	// # of parts of speech it can have
	BOOL HasType(uint part); 			// whether this word can be the given part of speech
	BOOL OnlyHasType(uint part); 		// whether it can ONLY be the given part of speech
	BOOL TypeIs(uint part) {return(typenow == part);}	// whether it is now the given type
	uint Type() {return typenow;}
	uint TypeAfter(uint part);
	uint nexttype();					// give word the next type in its list
	void SetTypenow(uint newtype);		// set to newtype, making it legal if necessary
	operator string() const;			// allows treating the TOK as its string

	// variables
	enum phrasetypes					// grammatical phrase types
	{
		//  0          1        2       3        4       5        8
		UNASSIGNED, NOUNPHR, VERBPHR, ADJPHR, ADVPHR, PREPPHR, SUBORD = 8
	};
	// type of info a phrase contains.
	// eventually, identify & create file lists of key words that can indicate why and how.
	// some words here (and in other lists) appear in more than one list.
	// may require a more careful search of surrounding words to resolve ambiguities.
	// why: because, since, as, for
	// how: by (with a verb ending in "ing"), with (a "howprep")
	enum infotypes
	{
		MISC,     			// 0 MISC OR UNASSIGNED OR UNKNOWN
		WHO,                // 1 SUBJECT
		DIDWHAT,            // 2 VERB
		TOWHOM,             // 3 DIR. OR INDIR. OBJECT
		WHEN,               // 4 WHEN
		WHERE,              // 5 WHERE
		WHY,                // 6 WHY
		HOW,                // 7 IN WHAT MANNER
		COND,				// 8 CONDITION (IF...)

		ANSYES,				// 9  A YES ANSWER, IN EFFECT
		ANSNO,				// 10 A NO ANSWER, IN EFFECT
		ANSIND,				// 11 INDETERMINATE (YOU ONLY KNOW IT WAS INTENDED AS AN ANSWER)

						//    TWO ADDL POSSIBILITIES FOR A TRAILING PHRASE
							//    BESIDES DIRECT OR INDIRECT OBJECT
		EQUIV,				// 12 EQUIVALENCY: [X] IS [NOUNPHRASE]
		DESCR				// 13 DESCRIPTION: [X] IS [ADJPHRASE]

	};
	string Alpha;		// spelling, as taken from user sentence (not MDB)
	string TypeList;	// simple type list as the string's chars
	int PhraseType;     // phrase type that the token is part of
	int InfoType;   	// type of information token contains: who, didwhat, etc.
	long AlphaID;		// the word's AlphaID in WTALK.MDB Phrases table.
	long DefID; 		// easy access to the word's definition, synonyms, attribute lists,
						// etc., as it's used in that location,
						// which makes various analyses based on "context" possible.
	// It should also help with parsing, because a word's usage usually is consistent
	// throughout a conversation, so if you can confidently determine it for one occurrence,
	// that's the safest assumption thereafter.
	// (unrelated note: sense-based operational definition member(s) go into MDB, too)

	// STATIC MEMBERS
	static SDDEHandler* Dde;	// a copy of the app's handler, for local use

	// STATIC FUNCTIONS
	static int tonumeric(string& type);				// was in DEF
	static string toalpha(int type);
	static uint IntToWordtypes(int i);

	static string phrasetoalpha(int type);
	static string infotoalpha(int type);

protected:
	uint typenow;	// current part of speech assigned.  you must set it with SetTypenow()

};
//----------------------------------------------------------------------------
// 							class TOK
//////////////////////////////////////////////////////////////////////////////
// an entry in a TOK similarity comparison table (for use in a TArray)
// the members are pointers to external, pre-existing TOKs so that after you
// have chosen the best match for a TOK, it is easy to delete from the array
// all the other potential pairings for that exact TOK (not TOKs that happen to
// have the same alpha spelling, etc.).  That is, a FACT might contain multiple instances
// of the same word, possibly even with the same part of speech, and we need
// to be able to identify an exact TOK.
//
class TOKComparisonEntry
{
public:
	TOKComparisonEntry(TOK* first = 0, TOK* second = 0);				// constructor
	TOKComparisonEntry(const TOKComparisonEntry&);                      // copy constructor

	BOOL operator == (const TOKComparisonEntry& other) const;           // equality
	BOOL operator != (const TOKComparisonEntry& other) const;			// inequality
	BOOL operator < (const TOKComparisonEntry& other) const;            // less than

	// functions
	BOOL RefersTo(const TOK* other) const;  	// whether either a or b matches other

	// variables
	TOK* a;
	TOK* b;
	double Similarity;

protected:
};
//----------------------------------------------------------------------------
// 							class TOKComparisonEntry
//////////////////////////////////////////////////////////////////////////////
#endif			// dic.h

dic.cpp

/*	dic.cpp         	8-25-01
	This is part of the WTalk project.
	Copyright (C)1993-2001 Steven Whitney.
	Published under GNU GPL (General Public License) Version 2, with ABSOLUTELY NO WARRANTY.
	Initially published by http://25yearsofprogramming.com.

Dictionary classes.

*/
#include "c:\bcs\library\ddemlapp.h"
#pragma hdrstop
#include "dic.h"

/////////////////////////////////////////////////////////////////////////////
// class TOK
//----------------------------------------------------------------------------
// Define static members.
SDDEHandler* TOK::Dde = 0;

//---------------------------------------------------------------------------
// constructor
TOK::TOK(string s)
{
Alpha = s;
typenow = UNASSIGNED;
PhraseType = TOK::UNASSIGNED;
InfoType = MISC;
AlphaID = 0L;
DefID = 0L;

LookupAlphaID();
LoadTypes();

}                          		// constructor
//----------------------------------------------------------------------------
// copy constructor
TOK::TOK(const TOK& other)
{
Alpha = other.Alpha;
AlphaID = other.AlphaID;
DefID = other.DefID;
typenow = other.typenow;
PhraseType = other.PhraseType;
InfoType = other.InfoType;
TypeList = other.TypeList;
}                            	// copy constructor
//----------------------------------------------------------------------------
// equal if they refer to the same DicEntry node AND their types (meanings) are the same
BOOL TOK::operator == (const TOK& other) const
{
	return((AlphaID == other.AlphaID) && (typenow == other.typenow));

// 	return(DefID == other.DefID);	// new, later
}
//---------------------------------------------------------------------------
// write
ostream& operator << (ostream& os, const TOK& )
{
string s("Function operator << (ostream& os, const TOK& ) does nothing.");
logerror(s);
return(os << s);
}
//----------------------------------------------------------------------------
// measures how similar two TOKs are.
// currently uses only the (case-insensitive) alpha spellings for the comparison.
// returns a value between 0 (no similarity at all) and 1.0 (identical)
// Measure is the same regardless of which TOK is "other".
double TOK::Similarity(const TOK& other)
{
// #error could pre-test for whether typenow is the same, but that assumes that both TOKs
// are in already-parsed FACTs, and it assumes more accurate type assignment than
// is now possible.
return(similarity(Alpha,other.Alpha));
}							// Similarity
//---------------------------------------------------------------------------
// this is used a LOT in fact.cpp. keep it fast.
TOK::operator string() const { return Alpha; }
//--------------------------------------------------------------------------
// tests by DDE query whether MDB Phrases table has words without definitions.
// currently unused fn, but keep for method.
BOOL TOK::AnyUndefinedWords()
{
string topic("SELECT DISTINCTROW Phrases.Alpha FROM Phrases "
			 "LEFT JOIN Defs ON Phrases.AlphaID = Defs.AlphaID "
			 "WHERE ((Defs.AlphaID Is Null));");
topic.prepend("WTALK;SQL ");
SDDEConv* chan1 = Dde->DDEInitiate("MSACCESS",topic);
BOOL anyleft = TRUE;   								// default in case of total failure
if(chan1)
{
	anyleft = chan1->DDERequest("NextRow");
	Dde->DDETerminate(chan1);
}
return(anyleft);
}               			//AnyUndefinedWords
//---------------------------------------------------------------------------
// look up and sets AlphaID from data in MDB, first adding it if necessary.
// returns AlphaID, as 0 if not found or DDE channel fails.
long TOK::LookupAlphaID()
{
AlphaID = 0L;
if(!Alpha.length())
	return(AlphaID);

// moved here from PreProcess because it ensures ALL new words get added;
// much easier to enter all possible types for a word if it's the only one shown.

// do look it up first because this is called for EVERY TOK; usually will be found.
string searchtopic("WTALK;SQL SELECT DISTINCTROW Phrases.AlphaID FROM Phrases "
				   "WHERE (Phrases.Alpha=\"%s\");");
searchtopic.substring("%s") = Alpha;    				// string substitution! see all.cpp
SDDEConv* chan1 = Dde->DDEInitiate("MSACCESS",searchtopic);
if(!chan1)
{
	logerror("topic rejected in LookupAlphaID");
	return(AlphaID); 							// failure: returns 0
}
if(chan1->DDERequest("NextRow"))
{
	fromstring(AlphaID,(string)(*chan1));

	// update usecount, lastused fields for this phrase.
	// the ability to update those fields for an individual DEF is still a long way off.
	// this allows starting counting now.  highest-use phrases should get highest priority
	// for manual entry of some definitions and attributes, etc., for experimenting.

	string t("[RUNSQL \"UPDATE DISTINCTROW Phrases "
			 "SET Phrases.UseCount = [Phrases].[UseCount]+1, Phrases.LastUsed = Now() "
			 "WHERE ((Phrases.AlphaID=%s));\"]");
	t.substring("%s") = tostring(AlphaID);
	chan1->DDEExecute("[SetWarnings 0]");  		// required to prevent msgs, even for DDE
	chan1->DDEExecute(t);
	chan1->DDEExecute("[SetWarnings -1]");  	// you want them ON for user data entry

	Dde->DDETerminate(chan1);
	return(AlphaID);
}
// REQUEST FAILED, SO ADD IT TO PHRASES.  chan1 is still open for use
// NOTE the required double quotes around the string literal!
string addtopic = "[RUNSQL \"INSERT INTO Phrases([Alpha]) VALUES (\"\"%s\"\");\"]";
addtopic.substring("%s") = Alpha;
chan1->DDEExecute("[SetWarnings 0]");  		// required to prevent msgs, even for DDE
chan1->DDEExecute(addtopic);
chan1->DDEExecute("[SetWarnings -1]");  	// you want them ON for user data entry

// OPEN (NOT RUN) A PRE-DESIGNED QUERY ON ALL PHRASES WITHOUT DEFS
chan1->DDEExecute("[OpenQuery PhrasesWithoutDefs]");
chan1->DDEExecute("[Maximize]");

// #error ENTRY SHOULD ACTUALLY BE DONE ON A FORM, WITH BETTER INSTRUCTIONS THERE.
// clunky way to get a parent, but an unowned ::msgbox becomes its own task in task-switch,
// and is confusing.

TWindow* w = GetApplicationObject()->GetMainWindow();
w->MessageBox("Enter at least one Type (part of speech) for each word shown.  "
			  "Use Alt+TAB or Ctrl+ESC to return here and close this box when you are done.",
			  "Please Task-Switch To MSAccess *NOW* To Enter Unknown Words",
												MB_OK | MB_ICONINFORMATION);

chan1->DDEExecute("[Close 1,PhrasesWithoutDefs]");	// MUST close it
Dde->DDETerminate(chan1);

// #error needs work: put in loop?
// TRY AGAIN TO LOOK IT UP.
chan1 = Dde->DDEInitiate("MSACCESS",searchtopic);
if(chan1)
{
	if(chan1->DDERequest("NextRow"))
		fromstring(AlphaID,(string)*chan1);
	else
		logerror("Alpha not found on 2nd search in LookupAlphaID");
	Dde->DDETerminate(chan1);
}
return(AlphaID);
}            				//LookupAlphaID
//----------------------------------------------------------------------------
// ok because the types list is no longer so permanent: it's only THIS tok.
// any later toks receive latest data.
// returns # of types loaded.  auto-sets typenow to 1st listed type.
uint TOK::LoadTypes()
{
TypeList.remove(0);
typenow = UNASSIGNED;
if(AlphaID)							// AlphaID of 0 = the flag of a 0-length string
{
	// (see also SetTypenow())
	// #error for each of these 2 sections (most common TYPE and most common DEF),
	// you could first do a simpler query: use WHERE DateDiff("d",[LastUsed],Now()) < 1
	// to find any entry used this same day; is a bit of trouble, but anything done
	// here to ensure a correct default can save lots of trouble choosing later.

	// unique types for Alphaid, by descending frequency of total encounters as each type
	// so the first type in TypeList is its most common use.
	string topic("WTALK;SQL "
				 "SELECT DISTINCTROW Defs.Type FROM Defs GROUP BY Defs.Type, Defs.AlphaID "
				 "HAVING ((Defs.AlphaID=%s)) ORDER BY Sum(Defs.UseCount) DESC , Defs.Type;");
	topic.substring("%s") = tostring(AlphaID);
	SDDEConv* chan1 = Dde->DDEInitiate("MSACCESS",topic);
	if(chan1)
	{
		uint u;
		while(chan1->DDERequest("NextRow"))
		{
			fromstring(u,(string)(*chan1));
			TypeList += (uchar)u;
		}
		Dde->DDETerminate(chan1);
	}
	if(!TypeList.length())					// ensure all words have 1 type minimum
		TypeList += (uchar)NOUN;			// NOUN most common
	SetTypenow(TypeAfter(0));
}
return(TypeCount());
}   						//LoadTypes
//----------------------------------------------------------------------------
// whether this word can be the given part of speech.
BOOL TOK::HasType(uint part)
{
for(int i = 0 ; i < TypeList.length(); i++)
	if(TypeList[i] == part)
		return(TRUE);
return(FALSE);
}                             	//HasType
//----------------------------------------------------------------------------
// whether this word can ONLY be the given part of speech.
BOOL TOK::OnlyHasType(uint part) { return((TypeCount() == 1) && HasType(part)); }
								//OnlyHasType
//----------------------------------------------------------------------------
// returns the next type in the list (if any), following the given type,
// or 0 if the end of the list was reached.
// if part == UNASSIGNED (0), it means you want the first listed type.
// Used by nexttype(), which is called by StateMachine.
// #error if that is its only use, maybe collapse into nexttype()?
uint TOK::TypeAfter(uint part)
{
if(!TypeList.length())
	return(0);
if(!part)
	return TypeList[0];
size_t u = TypeList.find(string((char)part));
if(u != NPOS)
	if(++u < TypeList.length())
		return(TypeList[u]);
return(0);
}                            	// TypeAfter
//---------------------------------------------------------------------------
// give word the next type in its list
// returns the new type OR zero if the type wasn't changed
uint TOK::nexttype()
{
uint newtype = TypeAfter(typenow);
if(newtype)                  		// only change typenow if next type was legal
	SetTypenow(newtype);            // otherwise it's left with the same type
return(newtype);
}                   			// nexttype
//---------------------------------------------------------------------------
// set typenow to the given part of speech, making that type legal for it, if necessary,
void TOK::SetTypenow(uint newtype)
{
if(TypeIs(newtype))
	return;
if((newtype != UNASSIGNED) && !HasType(newtype))
{
	string s("Can the word \"%s\" have the type %s?");
	s.substring("%s") = (string)(*this);
	s.substring("%s") = tostring(newtype);
	TWindow* w = GetApplicationObject()->GetMainWindow();
	if(w && w->MessageBox(s.c_str(),"Auto-assign Word Type?",MB_ICONQUESTION|MB_YESNO)==IDYES)
	{
		SDDEConv* chan1 = Dde->DDEInitiate("MSACCESS","WTALK");
		if(chan1)
		{
			s = "[RUNSQL \"INSERT INTO Defs ([AlphaID], [Type]) VALUES (%s, %s);\"]";
			s.substring("%s") = tostring(AlphaID);
			s.substring("%s") = tostring(newtype);
			chan1->DDEExecute(s);
			Dde->DDETerminate(chan1);
		}
		// even if sql failed, assign locally; don't call LoadTypes!  may have come from there
		TypeList += (uchar)newtype;
	}
	else
		return;
}
typenow = newtype;
PhraseType = TOK::UNASSIGNED;
InfoType = MISC;
DefID = 0L;
if(typenow == UNASSIGNED)
	return;
// GET INITIAL VALUES FROM THE MOST COMMON DEF OF THE TYPE JUST SET.
string topic = "WTALK;SQL SELECT DISTINCTROW Defs.DefID, Defs.InfoType, Defs.PhraseType "
		"FROM Defs WHERE ((Defs.AlphaID=%s) AND (Defs.Type=%s)) "
		"ORDER BY Defs.UseCount DESC , Defs.LastUsed DESC;";
topic.substring("%s") = tostring(AlphaID);
topic.substring("%s") = tostring(typenow);
SDDEConv* chan1 = Dde->DDEInitiate("MSACCESS",topic);
if(chan1)
{
	// having these may make some of the WordLists unnecessary,
	// except most words will have > 1 Def.
	if(chan1->DDERequest("NextRow"))
	{
		fromstring(DefID,chan1->Fields[0]);
		fromstring(InfoType,chan1->Fields[1]);
// 		fromstring(PhraseType,chan1->Fields[2]); // no: at Phrase() entry, all s/b UNASSIGNED
	}
	Dde->DDETerminate(chan1);
}
}                   		//SetTypenow
//---------------------------------------------------------------------------
// #error change these to look up corresponding values in MDB
//---------------------------------------------------------------------------
// returns the numeric translation of given textual word type, or 0 if not found.
// allows entering data into state machine data files as text, e.g. noun,conj
// this translation and some other things would be easier if types were kept in
// an array of class wordtype.
int TOK::tonumeric(string& type)
{
type.to_upper();
if(type == "NOUN") return(NOUN);
if(type == "VERB") return(VERB);
if(type == "ADJ") return(ADJ);
if(type == "ADV") return(ADV);
if(type == "DET") return(DET);
if(type == "PREP") return(PREP);
if(type == "TERM") return(TERM);
if(type == "CCONJ") return(CCONJ);
if(type == "SCONJ") return(SCONJ);
if(type == "EXCL") return(EXCL);
if(type == "PUNCT") return(PUNCT);
if(type == "VBHELP") return(VBHELP);
if(type == "NADV") return(NADV);
return(0);
}                    		// tonumeric
//---------------------------------------------------------------------------
// returns the appropriate text translation of given word type
// this is the complementary function to tonumeric, above
string TOK::toalpha(int type)      				// queried type
{
if(type == NOUN)   { return("NOUN"); }
if(type == VERB)   { return("VERB"); }
if(type == ADJ)    { return("ADJ"); }
if(type == ADV)    { return("ADV"); }
if(type == DET)    { return("DET"); }
if(type == PREP)   { return("PREP"); }
if(type == TERM)   { return("TERM"); }
if(type == CCONJ)  { return("CCONJ"); }
if(type == SCONJ)  { return("SCONJ"); }
if(type == EXCL)   { return("EXCL"); }
if(type == PUNCT)  { return("PUNCT"); }
if(type == VBHELP) { return("VBHELP"); }
if(type == NADV)   { return("NADV"); }
return("Illegal");
}                      			//toalpha
//----------------------------------------------------------------------------
// #error if unused, delete
uint TOK::IntToWordtypes(int i)
{
switch(i)
{
	case UNASSIGNED: return(UNASSIGNED);
	case NOUN:       return(NOUN);
	case VERB:       return(VERB);
	case ADJ:        return(ADJ);
	case ADV:        return(ADV);
	case DET:        return(DET);
	case PREP:       return(PREP);
	case CCONJ:      return(CCONJ);
	case SCONJ:      return(SCONJ);
	case EXCL:       return(EXCL);
	case PUNCT:      return(PUNCT);
	case TERM:       return(TERM);
	case VBHELP:     return(VBHELP);
	case NADV:       return(NADV);
	default:
		logerror("Illegal wordtype passed to DEF::IntToWordtypes(int i)");
		return(UNASSIGNED);
}
}                      		//IntToWordtypes
//---------------------------------------------------------------------------
// returns the appropriate text translation of the given numeric phrase type
string TOK::phrasetoalpha(int type)      // queried type
{
if(type == NOUNPHR)   { return("Noun"); }
if(type == VERBPHR)   { return("Verb"); }
if(type == ADJPHR)    { return("Adj"); }
if(type == ADVPHR)    { return("Adv"); }
if(type == PREPPHR)   { return("Prep"); }
if(type == SUBORD)   { return("Subord"); }
// if(type == start)  { return("start"); }	// an error if it occurs
return("----");
}
//---------------------------------------------------------------------------
// returns the appropriate text translation of the given numeric InfoType
string TOK::infotoalpha(int type)      // queried type
{
if(type == MISC)   	{ return("Misc"); }
if(type == WHO)   	{ return("Subject"); }
if(type == DIDWHAT)	{ return("DidWhat"); }
if(type == TOWHOM)  { return("ToWhom"); }
if(type == WHEN)   	{ return("When"); }
if(type == WHERE)   { return("Where"); }
if(type == WHY)   	{ return("Why"); }
if(type == HOW)   	{ return("How"); }
if(type == COND)   	{ return("Condition"); }
if(type == ANSYES)  { return("YesAnswer"); }
if(type == ANSNO)   { return("NoAnswer"); }
if(type == ANSIND)  { return("IndAnswer"); }
if(type == EQUIV)   { return("Equivalency"); }
if(type == DESCR)   { return("Description"); }
return("----");
}
//----------------------------------------------------------------------------
// 						end class TOK
//////////////////////////////////////////////////////////////////////////////
// class TOKComparisonEntry
//----------------------------------------------------------------------------
// constructor
TOKComparisonEntry::TOKComparisonEntry(TOK* first, TOK* second)
{
a = first;
b = second;
if(first && second)
	Similarity = first->Similarity(second);
else
	Similarity = 0.;
}                       	// constructor
//----------------------------------------------------------------------------
// copy constructor
TOKComparisonEntry::TOKComparisonEntry(const TOKComparisonEntry& other)
{
a = other.a;
b = other.b;
Similarity = other.Similarity;
}                			// copy constructor
//----------------------------------------------------------------------------
// equality
BOOL TOKComparisonEntry::operator == (const TOKComparisonEntry& other) const
{
	return((a == other.a) && (b == other.b) && (Similarity == other.Similarity));
}
//----------------------------------------------------------------------------
// inequality: sometimes it seems that the compiler makes this conversion automatically
BOOL TOKComparisonEntry::operator != (const TOKComparisonEntry& other) const
{
	return(!(other == *this));
}
//----------------------------------------------------------------------------
// less than: returns greater than, for a descending sort
BOOL TOKComparisonEntry::operator < (const TOKComparisonEntry& other) const
{
	return(Similarity > other.Similarity);
}
//----------------------------------------------------------------------------
// whether either a or b matches other
BOOL TOKComparisonEntry::RefersTo(const TOK* other) const
{
return((a == other) || (b == other));
}
//----------------------------------------------------------------------------
//						end class TOKComparisonEntry
//////////////////////////////////////////////////////////////////////////////

 

 

Valid HTML 4.01 Transitional Valid CSS
View content labeling at ICRA.
Copyright ©2007 Steven Whitney. Last modified 09/25/2007.