如https://projectlovelace.net/problems/blood-types/所述,我已经写出了解决血型匹配问题的解决方案。问题在于确定给定的接受者(在这种情况下为argv[1])是否会在一系列可用的供体(argv + 2)中找到匹配的输血。输入的血液类型:B+


输入的可用血液类型列表:A- B+ AB+ O+ B+ B-


输出:match


 #include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <err.h>

typedef struct {
    enum { O, A, B, AB } abo;
    enum { P, M } rh; 
} Blood;

const int abo[4][4] = {
    { O, O, O, O }, // O
    { O, A, O, A }, // A // *
    { O, B, O, B }, // B // *
    { O, A, B, AB }, // AB
};

const int rh[2][2] = {
    { P, M }, // P
    { M, M }, // M // *
};

Blood
parse(char *s){
    char rh0 = s[strlen(s)-1];
    char *abo0 = strdup(s);
    abo0[strlen(s)-1] = '// *';
    Blood b = {
        !strncmp(abo0, "O", 1) ? O
            : !strncmp(abo0, "A", 1) ? A
            : !strncmp(abo0, "B", 1) ? B
            : !strncmp(abo0, "AB", 2) ? AB
            : -1,
        rh0 == '+' ? P
            : rh0 == '-' ? M
            : -1,
    };
    return b;
}

int
main(int argc, char *argv[]) {
    if(argc < 3)
        err(1, "no arguments given\n");
    int n = argc - 2;
    char *a0 = argv[1];
    char **as = argv + 2;

    Blood b0 = parse(a0);
    Blood *bs = malloc(sizeof(Blood) * n);
    for(int i = 0; i < n; i++)
        bs[i] = parse(as[i]);

    for(int i = 0; i < 4; i++) {
        for(int j = 0; j < 2; j++) {
            for(int k = 0; k < n; k++) {
                if(abo[b0.abo][i] == bs[k].abo && rh[b0.rh][j] == bs[k].rh) {
                    printf("match\n");
                    return 0;
                }
            }
        }
    }
    printf("no match\n");
    return 1;
}
 


在我的测试中可以正常工作,但是使用了一些技巧。具体来说,标有q4312079q的行具有重复的枚举值,因为我找不到找到以血型为条件的循环大小的方法。大小一样?更一般地说,是否有更简单的血液类型匹配算法版本?

评论

错误:如果s为len 0,则您的解析函数将调用未定义的行为。

@ D.BenKnoble一个错误是程序在有效输入上的行为不佳时。据我所知,在任何地方都没有说明作为参数给出的空字符串是程序的有效输入。您也可能会抱怨,如果argc太大,则malloc可能会失败。我不希望运动程序能够抵御核武器。恕我直言,这最多是一个潜在的弱点(如果曾经在tis应用程序之外使用过parse()例程),而不是bug ... :)与正确处理不正确的输入相比,代码存在更严重的问题!

@CiaPan我在理论上大体上同意-但不注意这些错误(是的,错误:类型和文档对长度没有限制,因此未声明不是有效输入;更糟糕的是,它很可能成为一个问题)—不注意这些错误是一种危险的狂妄自大,它假定“正确”输入(这是一个坏主意),并且假定访问将以某种特定方式失败(例如,segfault)。馊主意。如果是在这里写的,那么在进入更大的系统之前,请学习一下!

您说测试中的代码“正常工作”。你能给他们看看吗?我怀疑您的程序不会通过。例如,您是否测试了A +型血液和AB +型献血者?

#1 楼

每个血液因子可以存在或不存在。可以使用A因子的一位,B因子的一位和Rh因子的一位对血液类型进行位编码。

"AB+"的血液类型解析为A + B + Rh == 7,将"O-"的血液类型解析为0,因为它不包含任何A因子,B因子或Rh因子。类型。补充受血者的血型以获取不良因素,并使用按位and操作测试供血者血液中是否不存在所有因素。

enum { A=1, B=2, Rh=4 };



如果要保留四种血液类型和Rh因子分别编码的枚举,则可以使用类似的编码。

请注意:

bool donor_matches = (donor & ~recipient) == 0;


将定义O=0, A=1, B=2, AB=3,因此您具有与上述类似的位分配。另一方面,Rh因子最好颠倒顺序:

enum { O, A, B, AB } abo;


以便M=0, P=1。现在,您可以表达匹配条件:

enum { M, P } rh;



最后,如果要保留最初定义的rh枚举,则:

bool donor_match = (donor_abo & ~recipient_abo) == 0 && (donor_rh & ~recipient_rh) == 0;


然后请注意,donor_rh >= recipient_rh将表示rh因子匹配,或者供体的限制性因子较低(数值较高)。

评论


\ $ \ begingroup \ $
我建议写0b001、0b010、0b100进行位编码,而不是枚举{A = 1,B = 2,Rh = 4};中的十进制数。这样可以更好地在代码中表达您的想法。
\ $ \ endgroup \ $
–乔纳斯·斯坦(Jonas Stein)
19年11月21日在21:56



\ $ \ begingroup \ $
@JonasStein我喜欢这个想法,但是从C11开始,它看起来只有十进制,十六进制和八进制常量是标准的。
\ $ \ endgroup \ $
– AJNeufeld
19年11月21日在22:45

\ $ \ begingroup \ $
不如@JonasStein的建议那么优雅,但是您可以使用移位:枚举{A = 1 << 0,B = 1 << 1,C = 1 << 2},尽管对于这么小的枚举(在处理大的位域之前,我已经使用过它,以确保我不会犯一个愚蠢的错误来手动定义常量)。
\ $ \ endgroup \ $
–芥末
19年11月22日15:56

\ $ \ begingroup \ $
@Wasabi也许不那么优雅,但符合标准。我赞成(怪异的C血型除外)。
\ $ \ endgroup \ $
– AJNeufeld
19年11月22日15:58

\ $ \ begingroup \ $
领域知识如何帮助实现优雅的出色示例。您知道潜在的逻辑是什么。 OP(和我)可能认为配对是随机的。
\ $ \ endgroup \ $
–格雷格·贝尔(Greg Bell)
19年11月27日在21:58

#2 楼

您的代码太复杂了。如果供体中存在AB+中的任何一个,则接受者中也必须存在q4312079q,q4312079q或q4312079q。这就是您所需要的。

bool is_compatible(const char* donor, const char* recipient)
{
    if(strstr(donor, "A") != NULL && strstr(recipient, "A") == NULL)
        return false;
    if(strstr(donor, "B") != NULL && strstr(recipient, "B") == NULL)
        return false;
    if(strstr(donor, "+") != NULL && strstr(recipient, "+") == NULL)
        return false;
    return true;
}

int main(int argc, char *argv[])
{
    for (int i = 2; i < argc; i++)
    {
        if (is_compatible(argv[i], argv[1]))
        {
            printf("match: %s\n", argv[i]);
            return 0;
        }
    }
    printf("no match\n");
    return 1;
}


评论


\ $ \ begingroup \ $
我认为,即使是像枚举一样简单的血型模型也是必需的。因为血液模型确实会发生变化,并且存在替代表示。尽管这肯定更小,更简单,但它会将血液分类逻辑硬编码到源代码中,而不是硬编码到有效血液类型的数据中。对于用于医疗用途的软件,这不一定是最好的方法,当然,这只是一种练习。
\ $ \ endgroup \ $
–疯狂
19年11月21日在23:54



\ $ \ begingroup \ $
@crasic,测试函数可以按照您的建议而变短,并在if(strstr(donor,types [index])&&!strstr(recipient,types [index]))返回false时进行简单循环;类型为“ AB +”。
\ $ \ endgroup \ $
–雷·巴特沃思(Ray Butterworth)
19年11月26日在16:21



#3 楼

忘记他们的要求,然后想一想真正的含义。

具有匹配项意味着至少一个施主字符串中不包含接收者字符串中没有的字符,除非占位符“ O”和“-”。

,请检查以下内容:

#include <malloc.h>
#include <stdio.h>
#include <string.h>

    int
main(int argc, char **argv) {
    if (argc>2) {
        auto int index;
        auto char **donor;
        auto char *donee = strcat(strcpy(malloc(3+strlen(argv[1])), argv[1]), "-O");
        for (donor=&argv[2]; *donor; ++donor) {
            for (index=0; index[*donor]; ++index)
                if (!strchr(donee, index[*donor])) break;
            if (!index[*donor]) return puts("Match"), 0;
        }
    }
    return puts("No match\n"), 1;
}


不必要的占位符“ O”和“- ”,应该已经从供体字符串中删除了,但是在这种情况下,将它们添加到收件人字符串中比较容易。类型。同样的程序也适用于Vulcans(Spok是“ T-”)。