鱼C论坛

 找回密码
 立即注册
查看: 3937|回复: 17

[见证历程] 从C到Python,漫漫学习路

[复制链接]
发表于 2018-8-13 07:38:16 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
  第一次在论坛发帖
  本人东北某辣鸡三本夕阳红专业。在课程上学习了C语言后觉得经脉皆通、神清气爽,因此打算自己把C语言剩下的内容学完,并且转向Python,日后图以草谋生计。无意间发现了鱼C这片神奇的土地,遂决定扎根在此,烦请大家一同见证我从C到Python的成长之路
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2018-8-13 15:41:51 | 显示全部楼层
花了好久研究这个问题:
题目内容:
NMEA-0183协议是为了在不同的GPS(全球定位系统)导航设备中建立统一的BTCM(海事无线电技术委员会)标准,由美国国家海洋电子协会(NMEA-The National Marine Electronics Associa-tion)制定的一套通讯协议。GPS接收机根据NMEA-0183协议的标准规范,将位置、速度等信息通过串口传送到PC机、PDA等设备。

NMEA-0183协议是GPS接收机应当遵守的标准协议,也是目前GPS接收机上使用最广泛的协议,大多数常见的GPS接收机、GPS数据处理软件、导航软件都遵守或者至少兼容这个协议。

NMEA-0183协议定义的语句非常多,但是常用的或者说兼容性最广的语句只有$GPGGA、$GPGSA、$GPGSV、$GPRMC、$GPVTG、$GPGLL等。

其中$GPRMC语句的格式如下:
    $GPRMC,024813.640,A,3158.4608,N,11848.3737,E,10.05,324.27,150706,,,A*50
这里整条语句是一个文本行,行中以逗号“,”隔开各个字段,每个字段的大小(长度)不一,这里的示例只是一种可能,并不能认为字段的大小就如上述例句一样。

字段0:$GPRMC,语句ID,表明该语句为Recommended Minimum Specific GPS/TRANSIT Data(RMC)推荐最小定位信息
字段1:UTC时间,hhmmss.sss格式
字段2:状态,A=定位,V=未定位
字段3:纬度ddmm.mmmm,度分格式(前导位数不足则补0)
字段4:纬度N(北纬)或S(南纬)
字段5:经度dddmm.mmmm,度分格式(前导位数不足则补0)
字段6:经度E(东经)或W(西经)
字段7:速度,节,Knots
字段8:方位角,度
字段9:UTC日期,DDMMYY格式
字段10:磁偏角,(000 - 180)度(前导位数不足则补0)
字段11:磁偏角方向,E=东W=西
字段16:校验值

这里,“*”为校验和识别符,其后面的两位数为校验和,代表了“$”和“*”之间所有字符(不包括这两个字符)的异或值的十六进制值。上面这条例句的校验和是十六进制的50,也就是十进制的80。
提示:^运算符的作用是异或。将$和*之间所有的字符做^运算(第一个字符和第二个字符异或,结果再和第三个字符异或,依此类推)之后的值对65536取余后的结果,应该和*后面的两个十六进制数字的值相等,否则的话说明这条语句在传输中发生了错误。注意这个十六进制值中是会出现A-F的大写字母的。

现在,你的程序要读入一系列GPS输出,其中包含$GPRMC,也包含其他语句。在数据的最后,有一行单独的
    END
表示数据的结束。

你的程序要从中找出$GPRMC语句,计算校验和,找出其中校验正确,并且字段2表示已定位的语句,从中计算出时间,换算成北京时间。一次数据中会包含多条$GPRMC语句,以最后一条语句得到的北京时间作为结果输出。

你的程序一定会读到一条有效的$GPRMC语句。

输入格式:
多条GPS语句,每条均以回车换行结束。最后一行是END三个大写字母。

输出格式:
6位数时间,表达为:
hh:mm:ss
其中,hh是两位数的小时,不足两位时前面补0;mm是两位数的分钟,不足两位时前面补0;ss是两位数的秒,不足两位时前面补0。

输入样例:
$GPRMC,024813.640,A,3158.4608,N,11848.3737,E,10.05,324.27,150706,,,A*50
END

输出样例:
10:48:13

这是中国大学MOOC上的题目,感觉有些有趣。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2018-8-13 15:42:58 | 显示全部楼层
写了好久,弄出来如下代码,不知为何系统验证总是不能通过,只好把它先贴在下面:
#include<stdio.h>
//&nbsp;&nbsp;$GPRMC,024813.640,A,3158.4608,N,11848.3737,E,10.05,324.27,150706,,,A*50
jiesu(char *p1)
{
    char *p2;
    p2=p1;
    if(*p2=='E')
    {
        p2++;
        if(*p2=='N')
        {
            p2++;
            if(*p2=='D')
                return 1;
        }
    }
    return 0;
}
kaishi(char *p1)
{
    char *p2;
    p2=p1;
    if(*p2=='$')
    {
        p2++;
        if(*p2=='G')
        {
            p2++;
            if(*p2=='P')
            {
                p2++;
                if(*p2=='R')
                {
                    p2++;
                    if(*p2=='M')
                        {
                            p2++;
                            if(*p2=='C')
                                return 1;
                        }
                }
            }
        }
    }
    return 0;
}
yihuo(char *p)
{
    int i,a;
    a=*p;
    for(i=1;*(p+i)!='*';i++)
    {
            a=a^*(p+i);
    }
    a=a%65536;
    return a;
}
int jiaoyan(char *p)
{
    int i,b=0;
    for(i=0;*(p+i)!='*';i++);
    i++;
    for(;*(p+i)!='\0';i++)
    {
        if(*(p+i)>='0'&&*(p+i)<='9')
        b=16*b+*(p+i)-'0';
        else if(*(p+i)>='A'&&*(p+i)<='F')
        b=16*b+*(p+i)-'A'+10;
    }
    return b;
}
dingwei(char *p)
{
    int i;
    for(i=0;*(p+i)!=',';i++);
    i++;
    for(;*(p+i)!=',';i++);
    i++;
    if(*(p+i)=='A')
        return 1;
    return 0;
}
shijian(char *p)
{
    int i,j,hour=0,minute=0,second=0;
    for(i=0;*(p+i)!=',';i++);
    for(j=1;j!=3;j++)
    {
        hour=10*hour+*(p+i+j)-'0';
    }
    hour=hour+8;
    if(hour>=24)
        hour=hour-24;
    if(hour>=10)
        printf("%d:%c%c:%c%c",hour,*(p+i+3),*(p+i+4),*(p+i+5),*(p+i+6));
    else if(hour>0)
        printf("0%d:%c%c:%c%c",hour,*(p+i+3),*(p+i+4),*(p+i+5),*(p+i+6));
    else printf("00:%c%c:%c%c",*(p+i+3),*(p+i+4),*(p+i+5),*(p+i+6));
}
main()
{
    char a[100][100],*p,*p2;
    int i=0,i_1=0,b,y,y_1,k=0,dy;
    for(i=0;i_1!=1;i++)
        {
            gets(a[i]);
            if(k==0)
                p=a[i];
            p2=a[i];
            if(kaishi(p)==1)
                k=1;
            if(jiesu(p2)==1)
                i_1=1;
        }
        p++;
        y=yihuo(p);
        y_1=jiaoyan(p);
        dy=dingwei(p);
        if(y==y_1&&dy==1)
            shijian(p);
}
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2018-8-13 15:56:36 | 显示全部楼层
本帖最后由 Simfisif 于 2018-8-13 15:59 编辑

最新结果!终于验证正确!
  回去重读了一下题目,发现系统的测试环节是输入了多条GPS输出语句。程序应当选取所有符合输出条件语句中最新的那一个进行输出,而我的原程序main函数部分是这样的:
  k=0;
  for(i=0;i_1!=1;i++)
       {
        gets(a[i]);
        if(k==0)
           p=a[i];
        p2=a[i];
        if(kaishi(p)==1)
           k=1;
        if(jiesu(p2)==1)
           i_1=1;
      }
这样一来,我的程序只能记住第一个字符串前有$GPRMC的语句。知道问题后,我将main函数部分修改如下,终于全对通过了系统测试:
main()
{
    char a[100][100],*p,*p2;
    int i=0,i_1=0,b,y,y_1,k=0,dy;
    for(i=0;i_1!=1;i++)
        {
            gets(a[i]);
            p2=a[i];
            if(kaishi(p2)==1)
            {
                p2++;
                y=yihuo(p2);
                y_1=jiaoyan(p2);
                dy=dingwei(p2);
                if(y==y_1&&dy==1)
                    p=a[i];
            }
            if(jiesu(p2)==1)
                i_1=1;
        }
            shijian(p);
}
总结一下,我对测试要求的输入形式还没有了解透彻,没弄明白其需求就盲目更改,这样一来果然容易摸不着头脑。在编写期间,还遇到了if中赋值这之类的错误。编程不可谓不是细心的工作啊。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2018-8-14 12:33:23 | 显示全部楼层
今天来学Python!
上了小甲鱼的003,发现Python的变量是在赋值的同时给出了定义的,这比C方便的多。在定义了不同的变量之后,这些变量也可以比较方便的做加法以及乘法,当然,不同的数据类型仍然不能做加法。
转义用‘\’就可以,字符串还能非常方便地用str=r'C:\n'的形式来自动转义。换行多的可以用‘’‘xxxxxxx’‘’来完成转义(用ctrl+Enter)换行。
原始字符串最后需要加\的,可以加“   ''\\   ”来完成。(这里不是很理解)

                               
登录/注册后可看大图
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2018-8-14 14:38:06 | 显示全部楼层
本帖最后由 Simfisif 于 2018-8-14 14:40 编辑

004程序写到最后成了和小甲鱼不太一样的样子,主要是不喜欢小甲鱼在程序中间又跑去改变量,总觉得不好,程序如下图:

                               
登录/注册后可看大图

有关课后习题:
1、&&的运算优先级大于and
2、有关逻辑短路我找到了相关链接:https://www.cnblogs.com/an9wer/p/5475551.html,该帖总结如下:
表达式从左至右运算,若 or 的左侧逻辑值为 True ,则短路 or 后所有的表达式(不管是 and 还是 or),直接输出 or 左侧表达式 。
表达式从左至右运算,若 and 的左侧逻辑值为 False ,则短路其后所有 and 表达式,直到有 or 出现,输出 and 左侧表达式到 or 的左侧,参与接下来的逻辑运算。
若 or 的左侧为 False ,或者 and 的左侧为 True 则不能使用短路逻辑。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2018-8-16 19:01:07 | 显示全部楼层
你牛B
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2018-8-18 18:14:25 | 显示全部楼层
听完005,把小甲鱼的游戏改进了一下,每次都有三次机会。猜完后可以选择是否重新玩一次(不玩输入0,玩就输入其他数字)
不过再改的时候出现了问题。楼主刚开始时把前面改完后,直接在print后面加了start=input()。结果发现print的start虽然是0,但是游戏还是会重新开始。结合005的数据类型,楼主终于明白:input输入的字符型,需要通过类型转换变成整型才可判断成功,因此程序如下:

                               
登录/注册后可看大图

在这之后楼主反思,如果不做类型转换,是否可以用字符型实现呢?
答案是可以的! 前面的while条件改为!=‘0’就行!(这相当于C语言里的char)
然后楼主把程序改成了这样:

                               
登录/注册后可看大图

#游戏升级为可以通过输入yes 或 no来判定是否继续游戏,如果两者都不是会打印error并自动结束
#对于游戏数据的输入也可以同理,但是笔者在此就不做更改了
写到这里,答主不禁感慨:python真是好用,竟然可以直接判断字符型数组是否等于某个特定值。这对于C语言的用户而言几乎是不可想象的。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2018-8-18 18:26:29 | 显示全部楼层

然后就断更了两天hhh
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2018-8-19 14:16:15 | 显示全部楼层
有关课后习题:4. 请用最快速度说出答案:not 1 or 0 and 1 or 3 and 4 or 5 and 6 or 7 and 8 and 9
这个就是前面提到的逻辑短路的问题,首先not 1 就是False,这样一来or才会去看后面的东西(若or左边是真则直接按or左边输出)。0是False,而后面接的是and,因此后面一直到or的所有的and都短路(即不看后面的and左右的数字)。一直到or,右边是3,为真,则按3输出,右边and 4为真,按4输出,后面是or,这时这里的or左边就是4,为真,or短路掉后面所有的and 和or ,因此程序输出4。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2018-8-19 15:13:00 | 显示全部楼层
感叹一下008这道题:
2. 假设有 x = 1,y = 2,z = 3,请问如何快速将三个变量的值互相交换?
答案:
x, y, z = z, y, x
我还在想用异或符号进行运算的事情,哎,python又打我脸了
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2018-8-19 15:22:29 | 显示全部楼层
补充个三元操作符的感悟:三元操作符说到底就是个变量,跟x,y没区别
x if(条件) else y
所以这里的x和y也一样可以用三元操作符来表示。
贴上神奇代码:
small=x if(x<=y and x<=z)else (y if y<=z else z)
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2018-8-19 15:52:49 | 显示全部楼层
听完了009的for循环,作为一个在C语言上疯狂用for循环的人,我竟然一时没有听懂python的for循环是个怎么回事。
上网搜了一些资料,自己看了看一些例子,大概是这样理解一下:
for循环的基本形式:
for x in a
其中,a是一个数组,x是一个指针。x默认指向a的首地址(就是数组a中的第一个数据),每经过一次for循环,x的指针地址就向前+1。当x指向空时(也即x指向最后一个数据并且循环完成之后),for循环便自动结束。
不知道对不对,但是好像姑且可以这么理解。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2018-8-19 16:01:39 | 显示全部楼层
  对上一楼进行一些说明。其实说x是个指针并不准确,应当说x是指针指向的数据(数字也好,字符也好,反正是数据而不是指针)也就是*p而不是p。在每次循环之后,x的地址都会向前+1,可以理解为p++。但是一定要注意的是x是*p而不是p。
  如果上面这一段看不懂的话,也可以这么理解:x是个变量,同时python自己悄悄定义了一个指针*p,并且把指针p的地址指向a的首地址,然后把指针的值*p赋给了x,在每次循环后,p都会p++,也就是它的地址向前+1,然后python重新把*p的值赋给x。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2018-8-19 20:02:32 | 显示全部楼层
  看到009的最后还是惊讶了一波!没想到for循环之中的x还有惊人的保真性!(姑且允许我这么说它)
  无论在循环程序中我们如何对x进行改变,在下一次循环开始之前,x还是会回到在上一次循环开始时的x值,并且在此基础之上在将其地址加上一个step值(默认为1)。
  说道这里不禁震惊,我仿佛看到了python的作者是如何利用c语言来写python的for循环一样。没错,其本质正是如我在上一楼所提到的:“x是个变量,同时python自己悄悄定义了一个指针*p,并且把指针p的地址指向a的首地址,然后把指针的值*p赋给了x,在每次循环后,p都会p++(应该说是p+=step),也就是它的地址向前+1(step),然后python重新把*p的值赋给x。”如此一来,在每次的循环中,你无论怎么改变x值,只要指针p的地址不为所动,在下一次循环开始之时,我们的x还是会被打回原样!
  奇也妙哉!

                               
登录/注册后可看大图
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2018-8-22 15:51:58 | 显示全部楼层
本帖最后由 Simfisif 于 2019-2-19 21:26 编辑

学完012,最大的感觉是——好晕,感觉python的语法只要貌似可行感觉一定可行(no,list1.sort().reverse()这个就不可行)。列表这个地方的语法感觉有些乱了,就简单梳理一下吧:
初始化列表:List1=[]
列表拼接:list1+=list2(或者直接上[]也行)
列表的增加:list1.append()(单参数),list1.extend([])(单列表),list1.insert()(双参数,位置,值)
列表的删改:list1.remove()(某个参数)(顺序第一个),del list1[](删除数据的位置),list1.pop()(删除并返回某个位置的数)
列表的排序:list1.sort(),()里默认值为reverse=False,为从小到大的顺排序,修改为reverse=True后为从大到小的逆排序
列表的倒置:list1.reverse(),()里可以添加颠倒的位置(19.2.19修改存疑,因为我不知道怎么添加了。上网看说是没有参数的,应该不能添加)
  说起来最让人感慨的还是这个地方:
member=[1,2,3]
member1=member[:]
member2=member
这两个地方虽然都是“复制”了member的列表,但是只有member1是真的复制,并且申请了新的内存了,member2这种形式的东西只是在原来的member列表上面增加了一个member2的标签而已。如果对member2进行修改的话,修改的还是member的值。
看来C语言里的一些习惯要改改了。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2018-8-22 16:56:42 | 显示全部楼层
关于013的元祖,让我印象比较深的有两个
一个是关于如何在封闭的元祖中添加元素的问题,元祖的添加(甚至是删改)元祖的元素的思路可以概括为:从元祖中利用“分片”,把元祖拆分为各个单独的部分,然后利用拼接,构造一个新的元祖,并且用新的元祖覆盖旧的元祖(小甲鱼的大概意思我理解为面向对象的高级编译器会自动删掉没有标签的数据,当然也包括元祖)。
所以这里会出现与我在楼上说的事情不一样的情况:

                               
登录/注册后可看大图

这里虽然number1是直接=number的。我是这样理解的:编译器是无论如何都不能修改元祖的内容的(它能做的只有覆盖),当它发现元祖上贴了两个标签但是它又要去改的时候,它就只能申请一个新的内存,把改了后的标签贴在新内存里的新数据上;另外一种可能是,元祖本身只能贴一个标签,当赋值=时,python会自动申请新的内存,定义一个新的元祖。
  第二件事就是元祖的灵魂在于“,”,也就是逗号,无逗号不元祖,当使用元祖拼接时(元祖可以向列表一样作为运算的参数进行拼接)(反正拼接又不更改它本身)(覆盖不算),拼接的东西如果是单元素,一定记得加括号。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2019-3-2 17:36:28 | 显示全部楼层
本帖最后由 Simfisif 于 2019-3-2 17:39 编辑

说几个章节的编程要点:
  1、局部变量与全局变量:主要是在函数中,如果要在函数中重新定义全局变量,一定记得“当作没有过这个变量”,编译器可能是先回完整地浏览函数,确定是否创建新的局部变量,如果要重新创建,那么在你定义前,编译器会假装该变量没有被定义。

2、进制转换:这里我写了一个m进制和n进制的转换,思路大家都会。出现了一个问题,如果输入的m进制数过大,则会出现未知错误(运算结果对不上)。

3、递归:递归是真的很精髓,一个“递”保证了其代码的深度,一个“归”保证它能回来,小甲鱼说得很对,如果递归没有了“归”,那就是错误。
不过递归也有其他的问题,那就是递归的效率汪汪比较低
附上用递归写的十进制与二进制的转换

4、字典:字典说实在的就是一个映射关系,使用大括号来定义字典,大括号内可以是“‘键’:‘值’”的模式。也可以是用dict(((‘键’,‘值’)))的伪装元祖形式定义。调用就直接以键的值进行调用就行。
还有其他的字典添加方法列图如下,直接添加新的键并定义值就行。

PS网络地址遇到问题了...过几天再补图
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-12-22 15:11

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表