问:
设配料表中的子配料,如“胆酸”,也由若干配料构成:“栀子 地龙 麻黄 前胡 枇杷叶”,“黄芩苷”的配料:“牛蒡子 胆酸 麻黄 紫苏”……,重构此函数,以类递归方式计算出更复杂的匹配度。
答:
<略>
问:
上面的函数以纯数组方式实现,无疑将问题复杂化了。现改用数据表Ingredient.dbf储存3000多种子配料信息,表结构为3个字段:name C (20),detail M,ID I。字段name是子配料名称,字段detail是以空格分隔的具体的子配料字符串,字段ID是整型数值。递归遍历此1000多种子配料表,若计算得到2个、3个、4个……或更多的子配料匹配度>=66.66%,则将其ID设为相同数值,即,将同一类的子配料,归为同一组。请重构函数,实现此算法。
答:
*----------注:deepSeek给出的代码有些复杂,我老人家此刻正在研读中

……
*-- 主函数:配料匹配与分组
FUNCTION GroupIngredients
LPARAMETERS tnThreshold
* 参数:tnThreshold - 分组匹配度阈值(51%)
* 功能:递归分析配料表,将匹配度>=阈值的配料归为同一组
LOCAL lnGroupID, lcSQL, lnCount
* 初始化分组ID
SELECT MAX(ID) FROM Ingredient INTO ARRAY laMaxID
lnGroupID = IIF(ISNULL(laMaxID[1]), 1, laMaxID[1] + 1)
* 创建临时表存储匹配结果
CREATE CURSOR TempMatches (;
Name1 C(20),;
Name2 C(20),;
MatchPercent N(5,2);
)
* 比较所有配料组合
lcSQL = "SELECT A.Name AS Name1, B.Name AS Name2 ";
+ "FROM Ingredient A, Ingredient B ";
+ "WHERE A.Name < B.Name"
// 避免重复比较
&lcSQL INTO CURSOR TempPairs
SELECT TempPairs
SCAN
* 计算两个配料的匹配度
lnMatch = CalculateMatch(TempPairs.Name1, TempPairs.Name2)
* 存储匹配结果
INSERT INTO TempMatches VALUES ;
(TempPairs.Name1, TempPairs.Name2, lnMatch)
ENDSCAN
* 根据匹配结果分组
SELECT * FROM TempMatches ;
WHERE MatchPercent >= tnThreshold ;
ORDER BY MatchPercent DESC ;
INTO CURSOR TempValidMatches
SELECT TempValidMatches
SCAN
* 查找两个配料的当前组ID
LOCAL lnID1, lnID2
SELECT ID FROM Ingredient WHERE Name = TempValidMatches.Name1 INTO ARRAY laID1
SELECT ID FROM Ingredient WHERE Name = TempValidMatches.Name2 INTO ARRAY laID2
lnID1 = IIF(_TALLY > 0, laID1[1], 0)
lnID2 = IIF(_TALLY > 0, laID2[1], 0)
* 分组逻辑
DO CASE
CASE lnID1 = 0 AND lnID2 = 0
* 两个配料都未分组,赋予新组ID
UPDATE Ingredient SET ID = lnGroupID ;
WHERE Name = TempValidMatches.Name1 OR Name = TempValidMatches.Name2
lnGroupID = lnGroupID + 1
CASE lnID1 = 0
* 配料1未分组,加入配料2的组
UPDATE Ingredient SET ID = lnID2 WHERE Name = TempValidMatches.Name1
CASE lnID2 = 0
* 配料2未分组,加入配料1的组
UPDATE Ingredient SET ID = lnID1 WHERE Name = TempValidMatches.Name2
CASE lnID1 <> lnID2
* 两个配料属于不同组,合并两组
UPDATE Ingredient SET ID = lnID1 WHERE ID = lnID2
ENDCASE
ENDSCAN
* 返回分组数量
SELECT COUNT(DISTINCT ID) FROM Ingredient WHERE ID > 0 INTO ARRAY laGroupCount
RETURN laGroupCount[1]
ENDFUNC
*-- 递归计算两个配料的匹配度
FUNCTION CalculateMatch
LPARAMETERS tcName1, tcName2, tnDepth
* 参数:tcName1, tcName2 - 要比较的配料名称
*
tnDepth - 当前递归深度
* 初始化参数
tnDepth = IIF(EMPTY(tnDepth), 1, tnDepth)
IF tnDepth > 5
&& 限制递归深度
RETURN 0
ENDIF
* 获取配料详情
LOCAL lcDetail1, lcDetail2
SELECT detail FROM Ingredient WHERE Name = tcName1 INTO ARRAY laDetail1
SELECT detail FROM Ingredient WHERE Name = tcName2 INTO ARRAY laDetail2
lcDetail1 = IIF(_TALLY > 0, laDetail1[1], "")
lcDetail2 = IIF(_TALLY > 0, laDetail2[1], "")
* 如果都没有子配料,直接比较名称
IF EMPTY(lcDetail1) AND EMPTY(lcDetail2)
RETURN IIF(UPPER(tcName1) == UPPER(tcName2), 100, 0)
ENDIF
* 分割子配料
LOCAL lnCount1, lnCount2, lnMatchCount, i, j
LOCAL ARRAY laItems1[1], laItems2[1]
lnCount1 = ALINES(laItems1, lcDetail1, " ")
lnCount2 = ALINES(laItems2, lcDetail2, " ")
* 计算直接匹配
lnMatchCount = 0
FOR i = 1 TO lnCount1
FOR j = 1 TO lnCount2
IF UPPER(ALLTRIM(laItems1[i])) == UPPER(ALLTRIM(laItems2[j]))
lnMatchCount = lnMatchCount + 1
EXIT
ENDIF
ENDFOR
ENDFOR
* 计算递归匹配
LOCAL lnRecursiveMatch, lnTotalWeight
lnRecursiveMatch = 0
lnTotalWeight = 0
FOR i = 1 TO lnCount1
FOR j = 1 TO lnCount2
* 跳过已直接匹配的
IF UPPER(ALLTRIM(laItems1[i])) == UPPER(ALLTRIM(laItems2[j]))
LOOP
ENDIF
* 计算子配料匹配度
LOCAL lnSubMatch
lnSubMatch = CalculateMatch(laItems1[i], laItems2[j], tnDepth + 1)
* 加权累加
IF lnSubMatch > 0
lnRecursiveMatch = lnRecursiveMatch + lnSubMatch
lnTotalWeight = lnTotalWeight + 1
ENDIF
ENDFOR
ENDFOR
* 计算总匹配度
LOCAL lnTotalMatch, lnAvgLength
lnAvgLength = (lnCount1 + lnCount2) / 2
IF lnTotalWeight > 0
lnRecursiveMatch = lnRecursiveMatch / lnTotalWeight
&& 平均递归匹配度
ENDIF
lnTotalMatch = lnMatchCount + lnRecursiveMatch
IF lnAvgLength > 0
lnTotalMatch = (lnTotalMatch / lnAvgLength) * 100
ELSE
lnTotalMatch = 0
ENDIF
RETURN ROUND(lnTotalMatch, 2)
ENDFUNC