V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
me15000
V2EX  ›  分享发现

有人用 goto 语句么?我是用它代替递归

  •  1
     
  •   me15000 · 2017-10-24 16:35:17 +08:00 · 10764 次点击
    这是一个创建于 2591 天前的主题,其中的信息可能已经有所发展或是发生改变。
    
    //递归
    void repeat(c) {
    
    	
    		//需要重复的代码
    	
    
    		if (xx) {
    			 repeat(xx);
    		}
    
    }
    
    //goto 代替递归
    void repeat(c) {
    
    	loop:
    		//需要重复的代码
    	
    
    		if (xx) {
    			goto loop;
    		}
    
    }
    
    
    
            //一个采集程序
            public void Run(string listurl)
            {
                using (var wc = new WebClient())
                {
                    loop:
    
                    string html = Encoding.GetEncoding("utf-8").GetString(wc.DownloadData(listurl));
                    var doc = new HtmlDocument();
                    doc.LoadHtml(html);
                    var nodes = doc.DocumentNode.SelectNodes("//ul[@id=\"data_list\"]/li/div/a");
                    for (int i = 0; i < nodes.Count; i++)
                    {
                        var node = nodes[i];
                        string link = "http://www..cn" + node.Attributes["href"].Value;
                        string title = node.SelectSingleNode("span[@class=\"sTit\"]").InnerText;
                        Save(title, link);
                    }
                    var nextnode = doc.DocumentNode.SelectSingleNode("//div[@class='page mb clearfixs']/em/following-sibling::a");
                    if (nextnode != null)
                    {
                        listurl = "http://www..cn" + nextnode.Attributes["href"].Value;
                        goto loop;
                    }
                }
            }
    
    第 1 条附言  ·  2017-10-24 18:03:57 +08:00

    之前用递归是因为很多时候需要遍历一个无限极分类, 还有就是采集的时候,需要一直下一页下一页,页数很多的时候,递归性能差 在使用goto 之前,我也尝试过 for 和 while,但是略显麻烦 所以我想到了goto ,用了之后,感觉很简约方便

          //一个采集程序,使用递归形式
            public void Run(string listurl)
            {
                using (var wc = new WebClient())
                {
    
                    string html = Encoding.GetEncoding("utf-8").GetString(wc.DownloadData(listurl));
                    var doc = new HtmlDocument(html);
                    var nextnode = doc.DocumentNode.SelectSingleNode("//div[@class='page mb clearfixs']/em/following-sibling::a");
                    if (nextnode != null)
                    {
                        var nexturl = nextnode.Attributes["href"].Value;
    
                        Run(nexturl); ///递归采集
                    }
                }
            }
    
    
          //一个采集程序,使用了goto
            public void Run(string listurl)
            {
                using (var wc = new WebClient())
                {
                    loop:  //////定义 goto lable 作为跳转标记
    
                    string html = Encoding.GetEncoding("utf-8").GetString(wc.DownloadData(listurl));
                    var doc = new HtmlDocument(html);              
                    var nextnode = doc.DocumentNode.SelectSingleNode("//div[@class='page mb clearfixs']/em/following-sibling::a");
                    if (nextnode != null)
                    {
                        listurl = nextnode.Attributes["href"].Value;
    
                        goto loop;///////////////////////这里是goto 到 loop
                    }
                }
            }
    
    
    //使用while
    public void Run(string listurl) {
    	using(var wc = new WebClient()) {
    		while (true) {
    			string html = Encoding.GetEncoding("utf-8").GetString(wc.DownloadData(listurl));
                            var doc = new HtmlDocument(html);
    			var nextnode = doc.DocumentNode.SelectSingleNode("//div[@class='page mb clearfixs']/em/following-sibling::a");
    			if (nextnode != null) {
    				listurl = nextnode.Attributes["href"].Value;
    			} else {
    				break; //跳出while
    			}
    
    		}
    	}
    }
    
    第 2 条附言  ·  2017-10-26 08:54:53 +08:00
    技术够用就行,达到目的就行,更多的时候是为了实现需求解决问题,语法、技术细节这些都不是核心问题,一个简单嘚 goto 都能扯出这么多问题,还让人编程么?如果每个技术细节都这样深究,那真不好做了,我只是用个 goto 而已,地球不会炸的,起码我得到的结果是,程序简洁明了,性能也很好,并不难懂,没出什么问题,这就是我要的
    第 3 条附言  ·  2017-10-26 09:58:09 +08:00
    后面的回复已经偏离我表达的初衷
    要不我建个群来喷吧: @all
    128 条回复    2017-10-27 09:04:44 +08:00
    1  2  
    zhujinliang
        2
    zhujinliang  
       2017-10-24 16:39:44 +08:00 via iPhone
    goto 替代不了递归吧
    b821025551b
        3
    b821025551b  
       2017-10-24 16:41:45 +08:00
    用 goto 的都烧死,自己爽了,别人要捋大半天。
    hualuogeng
        4
    hualuogeng  
       2017-10-24 16:41:59 +08:00
    递归是可以带参数传入的
    me15000
        5
    me15000  
    OP
       2017-10-24 16:42:04 +08:00
    @zhujinliang

    ```



    //一个采集程序 goto 代替递归
    public void Run(string listurl)
    {
    using (var wc = new WebClient())
    {
    loop:

    string html = Encoding.GetEncoding("utf-8").GetString(wc.DownloadData(listurl));
    var doc = new HtmlDocument();
    doc.LoadHtml(html);
    var nodes = doc.DocumentNode.SelectNodes("//ul[@id=\"data_list\"]/li/div/a");
    for (int i = 0; i < nodes.Count; i++)
    {
    var node = nodes[i];
    string link = "http://www..cn" + node.Attributes["href"].Value;
    string title = node.SelectSingleNode("span[@class=\"sTit\"]").InnerText;
    Save(title, link);
    }
    var nextnode = doc.DocumentNode.SelectSingleNode("//div[@class='page mb clearfixs']/em/following-sibling::a");
    if (nextnode != null)
    {
    listurl = "http://www..cn" + nextnode.Attributes["href"].Value;
    goto loop;
    }
    }
    }

    //如果使用递归
    public void Run(string listurl)
    {
    using (var wc = new WebClient())
    {

    string html = Encoding.GetEncoding("utf-8").GetString(wc.DownloadData(listurl));
    var doc = new HtmlDocument();
    doc.LoadHtml(html);
    var nodes = doc.DocumentNode.SelectNodes("//ul[@id=\"data_list\"]/li/div/a");
    for (int i = 0; i < nodes.Count; i++)
    {
    var node = nodes[i];
    string link = "http://www..cn" + node.Attributes["href"].Value;
    string title = node.SelectSingleNode("span[@class=\"sTit\"]").InnerText;
    Save(title, link);
    }
    var nextnode = doc.DocumentNode.SelectSingleNode("//div[@class='page mb clearfixs']/em/following-sibling::a");
    if (nextnode != null)
    {
    var nexturl = "http://www..cn" + nextnode.Attributes["href"].Value;

    Run(nexturl);
    }
    }
    }
    ```
    me15000
        6
    me15000  
    OP
       2017-10-24 16:45:17 +08:00
    @b821025551b 我一般仅用它代替递归,逻辑也很清晰,因为递归好像在 .NET 中有最大嵌套次数限制,而且递归比较消耗资源,所以用 goto 发现很好
    takanasi
        7
    takanasi  
       2017-10-24 16:48:30 +08:00
    @me15000 递归不能传参数和循环有什么区别?
    me15000
        8
    me15000  
    OP
       2017-10-24 16:51:33 +08:00
    @takanasi
    没懂你意思,我没说递归不能传参数
    我只想说用 goto 可以代替递归 很好用,递归有限制而且消耗资源
    zhujinliang
        9
    zhujinliang  
       2017-10-24 16:51:53 +08:00
    @me15000 为啥不用 while(true) 呢? for(;;) 也行啊
    me15000
        10
    me15000  
    OP
       2017-10-24 16:53:25 +08:00
    @zhujinliang 没 goto 清晰吧,而且 while(true){} for 有作用域范围,不好跳出去,要跳出去还要再写一些逻辑
    goto 倒是很省事,定义好 label,就可以跳到那里
    pcatzj
        11
    pcatzj  
       2017-10-24 17:00:04 +08:00
    @me15000 #8 他的意思是说,如果递归不传递参数的话,跟循环就一样了。所以递归的优势是参数,你用 goto,传递不了参数,跟循环基本没区别
    xupefei
        12
    xupefei  
       2017-10-24 17:02:54 +08:00   ❤️ 1
    高级语言都会把尾递归优化为 goto,所以 goto 我一般只在跳出多层循环的时候用。
    不过 goto 有个好处就是调试比递归方便,因为后者在 debug 编译模式下不会被优化从而导致压栈。
    hjc4869
        13
    hjc4869  
       2017-10-24 17:06:17 +08:00   ❤️ 1
    楼主,while 这里不好用,用 do while 不行吗?
    什么叫循环没 goto 清晰?省事,想跳到哪里就跳到哪里就是清晰吗?那叫面条代码。
    wwqgtxx
        14
    wwqgtxx  
       2017-10-24 17:07:21 +08:00 via iPhone
    @xupefei 并不是“都会”,很多解析式语言明确不支持尾递归优化
    SuperMild
        15
    SuperMild  
       2017-10-24 17:08:58 +08:00
    这样用 goto,真不如循环,循环一看就知道这是想循环,goto 本身不能表达这个意思,想让代码清晰还要加注释说明这个 goto 是为了循环。代码是给人类看的,在遇到瓶颈之前没必要做太细的优化。
    fqwerl
        16
    fqwerl  
       2017-10-24 17:09:23 +08:00 via Android
    用 goto 的基本可以打死了
    me15000
        17
    me15000  
    OP
       2017-10-24 17:09:33 +08:00
    @pcatzj 汗,不觉得我上面贴的代码 很方便么,goto 可以再一个 代码块里面,还用传参么?
    goto 之前先给变量赋值,赋值之后 goto 过去,变量存储的值还是在的

    伪代码:

    var 参数 a=1,参数 b=2;

    loop:

    参数 a++;
    参数 b--;

    goto loop;
    SuperMild
        18
    SuperMild  
       2017-10-24 17:11:13 +08:00
    而且从深层循环跳出去,很多语言都支持 break to label
    me15000
        19
    me15000  
    OP
       2017-10-24 17:12:14 +08:00
    @fqwerl
    @hjc4869

    ╮(╯▽╰)╭, 不要遵守教条,教条说是面条,意思是大量使用 goto 的情况下,然后就有人根据教条就 不敢用 goto 了
    hackpro
        20
    hackpro  
       2017-10-24 17:13:58 +08:00
    递归是多个堆栈
    你这个 goto 只有一个堆栈 可能会改写之前的状态参量
    tabris17
        21
    tabris17  
       2017-10-24 17:14:29 +08:00   ❤️ 2
    不谈场景就禁用 goto 的都是刷流氓

    goto 要比 do { break; } while (false);优雅得多
    jasontse
        22
    jasontse  
       2017-10-24 17:15:33 +08:00 via iPad
    这样写代码的人走夜路要小心
    me15000
        23
    me15000  
    OP
       2017-10-24 17:15:38 +08:00
    @SuperMild

    说实话,都用过,唯有 goto 好用
    me15000
        24
    me15000  
    OP
       2017-10-24 17:16:44 +08:00
    @hackpro 递归 10 万次试试,goto 表示无压力
    halfer53
        25
    halfer53  
       2017-10-24 17:16:54 +08:00
    除非是在 C 里面,任何 goto 都应该禁止。在 C 里面,因为 C 这个语言太接近于底层,没有很多高级语言的功能,所以不得不用 goto, 也只有仅有的几个地方可以使用 goto。比如

    ···
    int* ptr1 = malloc(100 * sizeof(int));
    if(ptr1 == NULL)
    goto err_ret;

    int* ptr2 = malloc(200 * sizeof(int));
    if(ptr2 == NULL)
    goto err_free_ptr1;

    int* ptr3 = malloc(300 * sizeof(int));
    if(ptr3 == NULL)
    goto err_free_ptr2;

    /** do staff **/

    free(ptr3);
    err_free_ptr2:
    free(ptr2);
    err_free_ptr1:
    free(ptr1);
    err_ret;
    return;
    ```
    pcatzj
        26
    pcatzj  
       2017-10-24 17:17:31 +08:00
    @me15000 #17 首先我是翻译楼上的,其次,你说的循环都能做到,没发现什么必用 goto 的理由,而且 goto 容易打乱别人读代码的节奏。没必要的话,最好不要乱跳代码,功能相同的代码应该聚集。
    me15000
        27
    me15000  
    OP
       2017-10-24 17:17:36 +08:00
    @hackpro 其实没懂你说的那种状况,我之前遇到递归到一定数量之后,内存溢出
    me15000
        28
    me15000  
    OP
       2017-10-24 17:20:37 +08:00
    @halfer53 其它语言既然提供这个语法,我觉得还是有用它的时候,我一般情况下只在一个 方法体内使用,目前没发现什么不好,估计哪天我写这样的代码,在一个技术型的公司里面该有一大波人吐槽我了,
    其实他们也没认真思考过这个问题,没有实践
    scys
        29
    scys  
       2017-10-24 17:21:54 +08:00
    慎用,加注释。
    viator42
        30
    viator42  
       2017-10-24 17:24:27 +08:00 via iPhone
    不建议用又不是不能用
    tabris17
        31
    tabris17  
       2017-10-24 17:25:52 +08:00   ❤️ 1
    几种可以使用 goto 的场景

    1、跳出多层循环

    2、return 前的收尾工作( try …… finally ……)

    3、异常处理(try …… catch ……)



    至于递归,大多数语言是有尾递归优化的,可以避免栈溢出。一般可以转换成 goto 的递归都可以写成尾递归形式。

    当然,LZ 说的场景并不适合用 goto
    hxsf
        32
    hxsf  
       2017-10-24 17:30:42 +08:00
    goto 可以代替流程控制语句

    你举的例子中不用 goto,也可以用 for,while 之类,

    递归能解开到循环的,可以用 goto 代替

    有的递归不能解开到循环

    比如:
    void ack(x) {
    if (x <=2) return 1;
    return ack(x-1) + ack(x-2)
    }

    上面这个,用循环的话得打表做, 仅仅是举个例子。
    wfd0807
        33
    wfd0807  
       2017-10-24 17:45:42 +08:00
    楼主这段代码的场景,可以用循环实现,也可以用递归实现;但是群主用了 goto 实现以后,硬说是代替了递归,这就是不太合适了;
    lightening
        34
    lightening  
       2017-10-24 17:48:32 +08:00   ❤️ 4
    Goto 有两个问题。

    第一是,如果你再加上一个跳出循环的判断,就有两个 Goto。那么读代码的人必须上下翻阅代码,找你的标记名字。如果有多个标记,还可能会冲突。

    第二个问题其实更重要,就是 Goto 不能显示你的意图。循环有很多种方式,大多数语言都有 for 循环和 while 循环。现代的语言很多又有 forEach。既然所有的功能都能用 while 实现,为什么还要 for 和 forEach ?就是因为这些语法是你思维方式的直接映射。因为人的大脑容量有限,所以我们只能把细节封装起来,从更高的层次抽象思维。一个好的程序要尽量分层描述“你想要什么”而不是“具体怎么做”。如果你想做的是重复一件事直到条件满足,那么 while 最合适了。如果你想做的是一件事几次,那么 for 循环最适合你了。很多人想对一个数组的每一个元素做某种操作,所以就有了 forEach。Goto 却无法表现出你的意图。读代码的人必须读完所有代码,才能琢磨到你想做什么;而不像其他三种循环操作,看一眼框架,虽然不知道你在循环内做了什么,但知道了你是做一个条件循环,还是一定次数的循环,还是对某个数组的每一个元素做了个操作。
    facetest
        35
    facetest  
       2017-10-24 17:50:31 +08:00 via Android
    说 goto 不好的,怕是被国内程序设计语言课程给洗脑了,去看看 linux 内核或者 nginx 源码就知道用得有多广泛。
    me15000
        36
    me15000  
    OP
       2017-10-24 17:52:56 +08:00
    @wfd0807 不假思索和尝试,不具备发言权,剥夺你发言权利 ^_^,目前我用得很爽
    hjc4869
        37
    hjc4869  
       2017-10-24 18:07:27 +08:00 via iPhone
    @me15000 我可没跟你说不能用,而是说你这个情况显然有更好的 solution,你应该多思考而不是一把梭怎么爽怎么写
    me15000
        38
    me15000  
    OP
       2017-10-24 18:12:09 +08:00
    @lightening VS F12 表示很方便

    icyalala
        39
    icyalala  
       2017-10-24 18:26:54 +08:00   ❤️ 1
    代码是写给人看的。

    你的代码如果只写给你自己一个人看,那你自己看懂就可以,随便写没问题。
    如果你的代码需要给别人看,除非有特殊理由,那就不要用“将会看你代码的人”普遍厌烦、普遍反对、额外增加他人思考时间才能读懂的方式来写。

    linux 内核、nginx 源码这些地方,goto 用得的不少,goto fail、goto cleanup 这类的用法很常见,也很清晰,这类开发者也常常接触这类代码,少有错误的使用方式。用 goto 没什么问题。

    你要说 .Net 。。
    JamesMackerel
        40
    JamesMackerel  
       2017-10-24 19:11:01 +08:00
    阁下的这种递归写法叫作“尾递归[1]”。

    所有的尾递归都可以被转换成循环,事实上,现代的很多编译器都会优化尾递归,使其变为循环来提高程序的执行效率,并减少内存的占用。

    GCC 在开启 O2 优化选项时会优化尾递归[2]。不过 Python 和 Java 似乎没有实现尾递归优化[3]。

    [1]: https://zh.wikipedia.org/zh-cn/%E5%B0%BE%E8%B0%83%E7%94%A8
    [2]: https://zhihu.com/question/20761771/answer/19201814
    [3]: https://zhihu.com/question/20761771/answer/92233964
    JamesMackerel
        41
    JamesMackerel  
       2017-10-24 19:12:19 +08:00
    另外,我想在编写生产环境的代码时,如果遇到尾递归,我认为我宁可把它优化为循环,或者就写一个尾递归,便于理解。
    whileFalse
        42
    whileFalse  
       2017-10-24 19:53:17 +08:00
    我在知道 for 之前用过一段 goto。
    后来知道了 for,觉得不如 goto 好用。

    那年我 14 岁。
    tnx2014
        43
    tnx2014  
       2017-10-24 19:56:22 +08:00
    用 goto 本身没什么问题,但是还是不推荐大面积的使用,更何况你也知道你这种结构就相当于循环,只有写汇编这种东西没办法不用的时候才使用,你现在觉得轻松是因为你只用那么一次,放弃循环语句,goto 满天飞试试看,你就知道多痛苦了,因为 goto 只能指示入口,其实还是从上往下顺序执行,有多个语句块时,有时候代码都不知道往哪儿写。

    推荐一个游戏,Human Resource Machine,用类汇编语言来实现相应的要求,楼主可以感受一下里面要求优化代码的痛苦。
    northisland
        44
    northisland  
       2017-10-24 20:01:07 +08:00
    邪教!
    geelaw
        45
    geelaw  
       2017-10-24 20:24:15 +08:00
    那个……您应该写成尾递归,聪明的编译器会直接优化成跳转。

    现在两份代码有明显区别,是因为递归调用结束之后还要 Dispose,外加递归版本会创建 没有必要多 个 WebClient。
    longaiwp
        46
    longaiwp  
       2017-10-24 21:33:24 +08:00
    @geelaw 显然 C#是不自带尾递归优化的,当然你用 R#可以实现手动尾递归优化,至于为什么 C#不带,我猜是做了有什么问题(微笑
    geelaw
        47
    geelaw  
       2017-10-24 21:56:20 +08:00 via iPhone
    @longaiwp 这段代码谈不上尾递归优化,因为那个递归并不是尾递归。

    不存在“ C# 没有尾递归优化”这种说法,因为理论上尾递归优化也可以发生在 JIT 的时候,而 JIT 并不知道这段代码是 C# 还是别的编译过来的。我不知道现在的 JIT 是否有这种优化。
    fox0001
        48
    fox0001  
       2017-10-25 01:19:23 +08:00   ❤️ 1
    你高兴就好…
    cnTangLang
        49
    cnTangLang  
       2017-10-25 06:30:57 +08:00 via Android
    如果代码是你自己去维护,你用 goto 没有问题。如果不是,尽量不要用。我的开发规范中,是严禁使用 goto 的。
    introom
        50
    introom  
       2017-10-25 08:30:20 +08:00 via Android
    楼主没有放弃用 goto 是好事情,很多人都是教条不敢用。不过,说实话楼主贴的代码让编译器 tco 或者用循环更直接。
    htfy96
        51
    htfy96  
       2017-10-25 08:50:40 +08:00   ❤️ 1
    业务逻辑代码尽量避免出现 goto
    backward goto 依旧是大忌,ng/linux 的 goto fail/cleanup 基本都是 forward 的,对控制流影响较小
    过早的优化是万恶之源

    所以总体不推荐这么搞,特别是.net
    ZhLTE
        52
    ZhLTE  
       2017-10-25 09:03:33 +08:00
    不建议 goto 破坏阅读体验
    exch4nge
        53
    exch4nge  
       2017-10-25 09:19:18 +08:00
    看标题,再看代码有点蒙... 标题要是用 goto 代替了循环,估计大部分人都能理解...

    首先 ,goto(LZ 用的方式),for,while,do while 其实编译后基本都一样;结构化编程开始流行后,有人提出来 goto(LZ 用的方式)都可以用后面几个方式替代,后面循环语句更有助于代码阅读,所以基本上 goto(LZ 用的方式)不怎么这么用了。

    不过 goto 在错误处理等等地方上比较好用,用在这方面倒是没啥太大问题。

    其次,递归代码都可以写成非递归的形式(模拟个栈,需要写循环语句),LZ 写的这种又是尾递归,尾递归更容易写成非递归,可以不用栈,直接用循环语句就能写。显然,也可以反着来,所有循环语句也可以写成递归形式,不过编译器如果没优化尾递归会爆栈罢了。

    所以,可以说,goto(LZ 用的方式),循环语句,尾递归方式原理上都一样,不过一般为了代码阅读性,使用循环语句是相对来说比较好的选择,而且一般人都会优先使用循环语句。(学院派 Lisp 里习惯用递归)
    linxl
        54
    linxl  
       2017-10-25 09:44:25 +08:00
    大多数脚本语言都没有尾递归优化
    airqj
        55
    airqj  
       2017-10-25 09:46:31 +08:00
    不考虑使用场景的使用都是耍流氓
    noli
        56
    noli  
       2017-10-25 10:27:51 +08:00 via iPhone
    用递归有栈溢出的风险,这是事实。
    但这不是使用 goto 的理由。
    没有什么使用 goto 的场合是找不到不用 goto 的替代方式的。而使用 goto,在很多原生语言例如 c++里,因为打破了符号作用域,会产生意外的效果。如果不是这些原生语言,那么 goto 比起正常使用其替代方式,带来的运行效率提升基本等于无。

    所以我不知道楼主有什么好感到得意的,我看到的只有无知。
    longaiwp
        57
    longaiwp  
       2017-10-25 10:32:47 +08:00
    @geelaw 不是完全没有,但是起码编译器是不会做的,至于 CLR 不一定会做
    geelaw
        58
    geelaw  
       2017-10-25 10:51:49 +08:00 via iPhone
    @noli goto 和作用域的关系是啥?
    msg7086
        59
    msg7086  
       2017-10-25 11:08:33 +08:00   ❤️ 1
    把递归重写成手工维护堆栈 /队列的循环是一个程序员的基本能力。
    比如说你楼顶附言里贴的这 3 段代码。
    如果我今天是码农职位面试官,我要你写一个这样的程序,你给我这 3 段里的任何一段,对我来说都是不合格的。

    因为你连 DFS/BFS 的基本写法都不知道。
    如果你不是科班毕业的,那我不怪你。
    如果你正常读完了计算机本科,写不出 DFS/BFS 应该的写法,那还是回去重新读一下大学课程比较好,特别是基础算法课。
    sgissb1
        60
    sgissb1  
       2017-10-25 11:23:00 +08:00
    说实话,开源代码里面用 goto 的最多。

    但是,goto 用的溜的,也不影响代码阅读和维护。但就目前来说大部分代码还是给人非常混乱的阅读理解和维护性。
    goto 能不用就不要用,除非高开源代码,有潜在可以甩手不管的机会,否则要自食其果的。
    msg7086
        61
    msg7086  
       2017-10-25 11:25:39 +08:00
    public void Run(string url)
    {
      using (var wc = new WebClient())
     {
       while (url != null)
      {
        string html = Encoding.GetEncoding("utf-8").GetString(wc.DownloadData(url));
        var doc = new HtmlDocument(html);
        url = doc.DocumentNode.SelectSingleNode("//div[@class='page mb clearfixs']/em/following-sibling::a")?.Attributes["href"]?.Value;
      }
     }
    }

    public void RunMulti(string url)
    {
      var urls = new Queue<string>();
      urls.Enqueue(url)
      using (var wc = new WebClient())
     {
       while (urls.Count > 0)
      {
        url = urls.Dequeue();
        string html = Encoding.GetEncoding("utf-8").GetString(wc.DownloadData(url));
        var doc = new HtmlDocument(html);
        Foreach(var node in doc.DocumentNode.SelectNodes("//div[@class='page mb clearfixs']/em/following-sibling::a"))
       {
         urls.Enqueue(node.Attributes["href"]?.Value;
       }
      }
     }
    }

    没有 VS,随便开了个编辑器手打的,而且 C#大概五六年没写了,如果有错请见谅。

    另外,你知道 Encoding.UTF8 吗?
    me15000
        62
    me15000  
    OP
       2017-10-25 11:59:57 +08:00
    @msg7086 看见你这种人就烦 Encoding.UTF8, Encoding.GetEncoding("utf-8"),我预留用来动态变换编码你个 xx
    me15000
        63
    me15000  
    OP
       2017-10-25 12:08:19 +08:00
    @msg7086

    说实话过于技术的人真的很 XX 无力吐槽,在你们这种公司上班肯定很遭罪
    100 种实现方法,都可以有,谁 tm 规定必须要你这样写,你规定的,你是法律,和法西斯没啥区别
    技术本身服务于需求,能达到目的就好,
    曾经有个公司花三年时间做一款产品,技术倒是做到位了,很好很牛逼,结果 tmd 资金没跟上倒闭了
    问倒闭原因,就是他们的 XX 技术主管过于技术过度技术,
    不懂的不同的时期量力而行,最终项目惨败,不同的项目对技术要求也不一样

    我就要一个留言板,够 100 个人用就行了,非要设计成 12306 的技术架构,我也是醉了
    这种人哪个公司用,哪个公司倒霉
    bumz
        64
    bumz  
       2017-10-25 12:17:10 +08:00   ❤️ 1
    goto 不能代替递归。

    goto + 栈可以。

    goto 不加栈就可以实现的递归代码,本身就用不着递归。

    ALGOL 58 引入 for 语句就是为了解决 goto 带来的麻烦,奈何 59 年后又有程序员用 goto 重新定义了循环,还贴上了实用主义的标签。

    我们只能说,你高兴就好。

    已添加到 V2EX_RECOMMENDED_BLACK_LIST。
    noli
        65
    noli  
       2017-10-25 12:46:39 +08:00
    @geelaw

    https://stackoverflow.com/questions/3179936/goto-out-of-a-block-do-destructors-get-called

    C++ 语义上保证 goto 语句, 对作用域内的对象生命周期是安全的。

    然而编译器的行为和直观上的感觉可能会不同;
    而 break continue 之类的就不会有这样的担忧,因为它们一次只会跳出一个 block。
    geelaw
        66
    geelaw  
       2017-10-25 12:58:08 +08:00 via iPhone
    @noli 完了,我觉得这个行为很直观
    sagaxu
        67
    sagaxu  
       2017-10-25 13:03:02 +08:00
    block++
    noli
        68
    noli  
       2017-10-25 14:53:28 +08:00
    @geelaw 很直观? 我给你找个实际例子,这段代码会泄露资源,资源类为 TypeA TypeB TypeC


    ```
    std::shared_ptr<TypeA> a(new TypeA);
    {
    bool error = doA(a);
    if (error)
    goto out_a;
    else
    {
    std::shared_ptr<TypeB> b(new TypeB);

    TypeD d;

    error = doB(b, &d);
    if (error)
    goto out_b;
    else
    {
    std::shared_ptr<TypeC> c(new TypeC);

    error = doABC(a, b, c);
    if (error)
    goto out_c;
    else
    goto out;
    }
    }
    }

    out_c:
    undo C

    out_b:
    undo B:

    out_a:
    undo A

    out:
    return ret;

    ```

    确实很直观吧。

    要不你建议,试试用 try ? XD XD

    try + goto 死得更快
    geelaw
        69
    geelaw  
       2017-10-25 15:40:13 +08:00
    @noli 我没有看出这段代码会泄露资源。实际上我也测试了全部 4 种情况,都不会泄露资源。您可能学了假的 C++。
    noli
        70
    noli  
       2017-10-25 15:57:51 +08:00
    @geelaw 能介绍一下你是怎么测的吗?
    geelaw
        71
    geelaw  
       2017-10-25 16:05:37 +08:00
    @noli http://codepad.org/MuZyhWWw

    无论输入 0、10、110、111,输出都是配对的。
    neoblackcap
        72
    neoblackcap  
       2017-10-25 16:23:10 +08:00
    C#递归性能很差吗?但是你可以写成尾递归的形式啊,C#有尾递归优化的,不用自己用 goto 来搞
    noli
        73
    noli  
       2017-10-25 16:59:15 +08:00
    @geelaw 是我复制错代码了,给的是已经修改过后的。

    本来应该是把 else 以及后面的 {} 都去掉,至少让你品尝一下 cross initialization 的。
    geelaw
        74
    geelaw  
       2017-10-25 17:14:45 +08:00
    @noli 完全去掉 else (以及它产生的作用域),会导致代码是错误的,而不是正确且产生令人惊奇的结果的。
    msg7086
        75
    msg7086  
       2017-10-25 17:29:08 +08:00   ❤️ 5
    @me15000 我们公司成立了差不多 10 年了。
    10 年里几乎每一个程序员都得过且过,坏的代码不修,新写的代码稀烂,没有任何代码审核,连 Git 都不会用,要我手把手教。
    结果呢?投资的 1500 万美金烧完了,程序连稳定跑都跑不起来,一崩溃就得抹掉数据库重新做,因为连原来设计系统的程序员都不知道哪些行哪些字段里应该填什么,索性全抹了重新初始化。10 万刀一套软件卖给客户,客户三天两头过来骂娘问为什么又崩溃了,为什么一个 UI 界面刷新一下要 15 分钟。然后我们的客服也来骂开发团队,为什么远程客户机器刷一个页面要 15 分钟才能刷完。

    我进了公司以后我怂恿老大把他们全开了,现在从上到下所有人全走了,只剩我和一个客服两个人写代码,半年做完基本功能,给 CEO 演示的时候 CEO 已经要哭了。


    何况你这代码说白了只是个广度优先搜索,说得难听点我初三就会写了,你偏要当成是多么高深的技术,我只能说我们根本就不是一路人。你心里想的是哇还有 goto 这东西,而我心里想的是公司这卖几十万一套的软件怎么做到不崩溃怎么找到更多客户拉到更多投资最后怎么上市然后我可以捞一票。

    不过度追求技术是应该的,作为程序员应该也学过这句话,叫过早的优化是罪恶的根源。
    但是连大学本科基本算法都不懂的,贵司还是留着自己享受吧,我们已经用了好多年这样的人了,再用下去,这公司怕是要上天了。

    如果你看不惯做技术的,那把我 Block 了便是。只不过这帖子除开你的 goto,剩下的内容营养价值还挺高的,JIT,尾递归优化什么的,如果你能更虚心些去学习他们提到的知识点的话,也算是能提高下自己的姿势水平,这帖子也算没白开。

    就这样吧,你自便了。
    noli
        76
    noli  
       2017-10-25 17:30:00 +08:00
    @geelaw

    是否错误,取决于你用哪个编译器,以及 flag。
    而且这里也涉及到了 C 和 C++ 的差异,如果有 C 和 C++ 混用的话,效果更显著。

    算了,反正你觉得 开心就好。
    noli
        77
    noli  
       2017-10-25 17:36:46 +08:00
    @geelaw 还是再补充一句。

    即使是 C++ 里面,去掉大括号去掉 else
    TypeD d; 这一句也并不一定就会编译错误。

    视乎你的 TypeD 怎么写。

    所以这种一不留神就会踩坑的代码,我真的不觉得直观。

    我保留我的意见。
    geelaw
        78
    geelaw  
       2017-10-25 18:06:38 +08:00 via iPhone
    @noli non-trivial 构造被跳过本来就是 ill-formed,这样 argue 跟写 i+++i++ 也能编译是一样的。
    noli
        79
    noli  
       2017-10-25 18:19:05 +08:00
    @geelaw

    所以不用 goto 就没有这种连声明个指针 都是 ill-formed 的破事儿
    就跟不要写 i+++i++ 这种破代码是一样的道理。
    geelaw
        80
    geelaw  
       2017-10-25 23:07:37 +08:00
    @noli 声明的并不是指针而是对象啊……你在想啥?
    noli
        81
    noli  
       2017-10-25 23:19:18 +08:00
    @geelaw

    我又用错词了,应该说,“不用 goto 就没有这种连初始化个指针都是 ill-formed 的破事儿"。
    虽然在你的代码里面 TypeD 是个 struct

    但谁也没法阻止 typedef TypeC* TypeD; 对吧

    #include <iostream>


    int main(int argc, char **argv)
    {
    if (argc > 0) {
    goto PRE_EXIT;
    }
    int * some_ptr;

    // initializing some_ptr will upset the compiler
    // int * some_ptr = nullptr;

    PRE_EXIT:
    std::cerr << argc;
    return 0;
    }
    geelaw
        82
    geelaw  
       2017-10-25 23:25:26 +08:00
    @noli 如果是 POD,你可以把初始化替换为不初始化+赋值啊……这样你就很明确了。
    ryd994
        83
    ryd994  
       2017-10-26 06:22:58 +08:00 via Android
    @Livid #61 #62 楼主这已经很不礼貌了
    问题本身就是很基础的问题,楼下大家的意见也很明确,楼主完全没有在听
    我认为这贴已经在浪费大家的时间
    建议 /go/chamber
    ryd994
        84
    ryd994  
       2017-10-26 06:34:03 +08:00 via Android
    @facetest #35 少拿 linux 和 nginx 说事。人家是多年的老程序员的,知道用 goto 意味着什么。规矩都差不多:原则上不允许用 goto,除了(错误处理&不影响可读性)
    还有个例外就是 linux 进程调度部分:那部分已经好多年没动过了,现在也没几个人敢动。现在只能修修补补凑合用,等着哪天哪个大牛忍不下去重写
    ryd994
        85
    ryd994  
       2017-10-26 06:44:08 +08:00 via Android
    @introom #50 初生牛犊不怕虎
    绝大多数项目的要求是:goto 仅用于错误处理,且不得影响可读性。禁止 goto 参与主要控制逻辑。
    https://stackoverflow.com/questions/46586/goto-still-considered-harmful
    这么喜欢 goto,怎么不写汇编去啊
    carlclone
        86
    carlclone  
       2017-10-26 06:57:05 +08:00 via Android
    楼主连续几个帖子被喷也是不容易,反省一下自己吧不如
    me15000
        87
    me15000  
    OP
       2017-10-26 08:06:09 +08:00 via Android
    @ryd994
    心里仍然有喷你的欲望,不礼貌建立在对方不礼貌的前提下,还有你这种动不动就找人举报的人,不够 man 和上小学幼稚园的小朋友有的一拼,

    上面的观点并没有说服我
    有人说老程序员这样做,所以就不用,那你其实也不知道为什么

    还有人说 nginx 什么里面用来处理错误,所以只用来处理错误时候用 goto,这有点牵强吧?

    在我看来仍然可以用,除非这样用很难被普通人理解,或者用了会出现严重 bug

    目前最主要的矛盾是:大多数人没这样用过,所以不去用,还有国内初识编程时对 goto 的认知


    @carlclone 日心说那家伙还被火烧了呢,帖子里的观点如果有人愿意认真思考的话,没什么道理可言,要么是模仿要么是教条主义
    introom
        88
    introom  
       2017-10-26 08:16:57 +08:00 via Android
    @ryd994 是是是,对对对,初生牛犊,不像你曾经沧海云卷云舒。 我的五十楼的回帖很中肯,也说了 lz 的实例代码使用 goto 不恰当,这样来强喷就没什么意思了。我也希望你指点指点,不过贴一个日经的链接就索然无味了。
    chiu
        89
    chiu  
       2017-10-26 08:30:47 +08:00 via Android
    有用 setjump(),longjump()的吗
    ragnaroks
        90
    ragnaroks  
       2017-10-26 08:31:10 +08:00
    @me15000 mshtml.dll?这个最好不要用,或者先自己另外获取了 html 后用字符串生成 htmldocument,ms 自己的那个解析经常出现意外
    me15000
        91
    me15000  
    OP
       2017-10-26 08:34:59 +08:00 via Android
    @msg7086
    说实话我并没有说这是高深的技术,原本觉得这样写蛮有意思,就分享一下,你气势汹汹说人无知,我说你无知,你说你们公司那样,说明你们公司并没有完善的管理机制,你是一个人搞定了没错,但你们公司发展不大,因为只有你一个人可以搞,我觉得也不是什么值得骄傲的事情,我认为任何产品或技术都应该追求简约,而不是需要十几年的积淀才能做好,那对大多数公司来说不划算
    me15000
        92
    me15000  
    OP
       2017-10-26 08:35:36 +08:00 via Android
    @ragnaroks 我写的控制台程序
    me15000
        93
    me15000  
    OP
       2017-10-26 08:37:30 +08:00 via Android
    @ragnaroks 不是 ms 是开源的
    ragnaroks
        94
    ragnaroks  
       2017-10-26 08:45:01 +08:00
    @me15000 我看到 new HtmlDocument 以为是萎软 mshtml.dll,我用它做大人站解析的时候动不动无法获取数据或者获取数据途中崩溃,实在没办法用 HAP 才完美
    me15000
        95
    me15000  
    OP
       2017-10-26 08:47:54 +08:00 via Android
    @noli 又是一枚自认为很厉害的技术,说实话我倒是没感到得意,反而觉得你挺臭屁的
    superkeke
        96
    superkeke  
       2017-10-26 08:52:16 +08:00
    有时候会用 goto 来进行错误处理,可以很好的处理内存问题
    paulagent
        97
    paulagent  
       2017-10-26 09:08:44 +08:00
    好久没看到引战帖子了
    facetest
        98
    facetest  
       2017-10-26 09:15:02 +08:00 via Android
    @ryd994 谁的规矩,是一部分人的规矩还是所有人的规矩?还老程序员知道 goto 意味着什么,意思就是小程序员我若我有理咯。你发的 stackoverflow 里,也摘录了 linus 的话,人家就觉得 goto 挺好。
    msg7086
        99
    msg7086  
       2017-10-26 09:23:37 +08:00
    @me15000 气势汹汹?
    我花了时间帮你改进了代码还帮你加了功能,这就是你评价别人的态度咯。那我应该评价你啥?恩将仇报吗?

    信不信随你,但是我们这比我水平好的人太多太多了。
    我们大学操作系统课程大作业是写一个 Linux 的 USB 设备驱动。我在小组里别说写代码了,就连写报告的水平都轮不上。
    一个班级毕业出来的人,几乎每一个都比我厉害得多。我 GPA 3.0 毕业,不到 3.0 连毕业文凭都拿不到,我想那些 GPA 超过 3.0 的学霸们那数量肯定是不少了。

    一个公司只有我一个人可以搞?那的确不是什么值得骄傲的事情。幸好像我这种水平只是代表程序员这个岗位的最低要求,想要招人,比我厉害的到处都是。之前招的那批程序员都是公司为了省钱从国外招的,什么印度的,乌克兰的,东欧小国的都有,他们就是因为缺乏这种匠人精神才把公司搞垮成这样。老老实实一年十万招点比我水平更好的进来,公司早就上市了也说不定。

    你说公司搞不大,那我们谈谈大公司。亚马逊我面过(很可惜人家看不上我这水平),电面 3 道算法题,去公司面试 4 道算法题 2 道设计题,加起来 8-9 轮面试。不说别的,你跟考官说你 BFS / DFS 不会写,只会写递归,而且不知道堆栈溢出是什么玩意,怕是电面都过不了。我那时候第一轮电面是个中国人,起手让我写一个堆排序的类,白板直接手写代码,这要算的话大概是大四或者研究生课程的东西了。

    再大点的公司,谷歌?我根本就没敢去面。以前一个小基友是普渡学霸,毕业了跑去谷歌开发某个汽车项目,他给我做过一次简单面试,把我打得满地找牙。不知道你是有多自信才觉得大公司不需要有高技术的人的。

    最后,你认为任何产品或技术都应该追求简约而不需要十几年的沉淀?
    我只能发一个我不知道该对你做出什么样的表情.jpg 的表情了。
    真以为计算机这个行业是看看书看看视频 7 天学成程序员啊。
    我跟你说,v2ex 技术出身的人多,你要是讨厌有技术的人,我劝你少来这里,否则总有一天你会被气炸的。
    xomix
        100
    xomix  
       2017-10-26 09:26:43 +08:00
    po 主祭出大杀器就是想让大家活跃些吗?

    goto 能不能用?能! 除了 java 这种保留关键字又不实现的语言之外,只要有这个关键字就能用。if goto 是一个在面向过程编程时很常见的语法。

    goto 好不好用?这个不好肯定回答,但是用对了一定事半功倍。再好的东西滥用了就变得不好用了。

    为什么大一些的项目组尽量禁用 goto ?大项目组禁用的东西很多,约束也很多,主要是为了未来你不再维护这些代码的时候他人来到可以快速上手维护代码,所以牺牲一些效率和代码行数,获得更好的可读性和维护性对他们来说是划算的甚至是最优的。


    所以…………个人项目你就是不想写 for 循环写成 goto 也行啊,只要你自己维护的时候别觉得眼晕就行。
    1  2  
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3265 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 30ms · UTC 12:32 · PVG 20:32 · LAX 04:32 · JFK 07:32
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.