递归,堆栈,迭代器,yield return;;

zhanglong19891129 2012-09-13 12:05:49
!):递归是不是就是用的堆栈来计算的啊?我的理解是这样的,大侠指点下...看到sp1234 提到才想到 递归有堆栈的联系..:: 假如一个递归算法,需要递归100次... 然后pop.push(100)......pop.push(1)..这样先堆好了 在 pop.drop(1)............ pop.drop(100),这样来计算的....假如drop(1)执行的时间复杂度是N
那么时间复杂度是多少?100N?....
!!):迭代器 是不是与指针有关..比如存储数据: 是通过指针++来逐步存储数据的...迭代器这个请详细解释下;;;
!!!):yield return a 是不是就是在当前迭代快中 add a 这个值 啊.....
假如是这样的话,我完全可以用List<T> 这样的链表来代替啊... for(i....) list[T] 实例.add(值)..那么迭代器感觉可以不需要啊
...全文
230 14 打赏 收藏 转发到动态 举报
写回复
用AI写文章
14 条回复
切换为时间正序
请发表友善的回复…
发表回复
xboxeer 2012-09-13
  • 打赏
  • 举报
回复
用yield的好处是你可以再迭代的时候同时完成其他操作 效率更高
[Quote=引用楼主 的回复:]
!):递归是不是就是用的堆栈来计算的啊?我的理解是这样的,大侠指点下...看到sp1234 提到才想到 递归有堆栈的联系..:: 假如一个递归算法,需要递归100次... 然后pop.push(100)......pop.push(1)..这样先堆好了 在 pop.drop(1)............ pop.drop(100),这样来计算的....假如drop(1)执行的时间复杂度是N
那……
[/Quote]
xboxeer 2012-09-13
  • 打赏
  • 举报
回复
用yield的好处是你可以再迭代的时候同时完成其他操作 效率更高
[Quote=引用楼主 的回复:]
!):递归是不是就是用的堆栈来计算的啊?我的理解是这样的,大侠指点下...看到sp1234 提到才想到 递归有堆栈的联系..:: 假如一个递归算法,需要递归100次... 然后pop.push(100)......pop.push(1)..这样先堆好了 在 pop.drop(1)............ pop.drop(100),这样来计算的....假如drop(1)执行的时间复杂度是N
那……
[/Quote]
victo7 2012-09-13
  • 打赏
  • 举报
回复
“那么时间复杂度是多少?100N?....” --- 1
不需要循环,直接取值,因为它有一个栈顶指针啊,任何时候操作都是在栈顶
threenewbee 2012-09-13
  • 打赏
  • 举报
回复
之前我说错了,O(n)和o(n)是不同的。
O(n)表示的是函数数量级的渐近上界。因此100N这个说法是不对的,不应该有常数项。
zhanglong19891129 2012-09-13
  • 打赏
  • 举报
回复
[Quote=引用 1 楼 的回复:]

注意你的用词,O(n)和O(N)是不同的。
yield return本质上是一个状态机,C#会创建一个线程,并且每次迭代后都会将这个线程挂起,再次调用则继续。它向调用者返回当前迭代值,但是它不会保存到存储中,和List不同,也不能随机访问迭代器。
[/Quote]
第3个问题解决了 3Q .. 1 ,2 问题 求继续解答...注意你的用词,O(n)和O(N)是不同的。这个啥意思?
threenewbee 2012-09-13
  • 打赏
  • 举报
回复
注意你的用词,O(n)和O(N)是不同的。
yield return本质上是一个状态机,C#会创建一个线程,并且每次迭代后都会将这个线程挂起,再次调用则继续。它向调用者返回当前迭代值,但是它不会保存到存储中,和List不同,也不能随机访问迭代器。
threenewbee 2012-09-13
  • 打赏
  • 举报
回复
一般来说,一个函数的调用过程是,将调用者的地址保存,参数压入堆栈,函数执行,然后恢复堆栈,返回结果,转到调用者继续执行。
而yield return则可以自动产生一个匿名类,这个类的迭代器的current和movenext方法有些不同——它会将你的原来yield return的那个方法“拆开来”,使得它可以分段执行——在每次movenext被调用的时候,这个函数就会执行,直到遇到yield return,它会把函数的当前状态保存下来,并且挂起执行(事实上内部是用了一个线程)。当调用current的时候,就返回yield return的内容。当再次调用movenext的时候,就会恢复之前的函数调用,运行直到再次遇到yield return或者运行完,如果是运行到yield return,和之前说的一样,如果运行完也没有遇到yield return,movenext就会返回false。

为什么说这个过程是延迟执行的——很容易理解。因为当你遍历的时候,只有在调用movenext时候,这个函数才会执行一小部分,而不是像list那样,先运行完成,得到所有结果,装入内存,再开始迭代。
threenewbee 2012-09-13
  • 打赏
  • 举报
回复
这个问题说来话长,这次我再展开foreach,看看你能不能琢磨出什么。

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication1
{
class OneToThree : IEnumerable<int>
{
public class OneToThreeEnumerator : IEnumerator<int>, IEnumerator
{
private int _current = 0;

public int Current
{
get { return _current; }
}

public void Dispose()
{
//这里我们不需要清理
}

object System.Collections.IEnumerator.Current
{
get { return _current; }
}

public bool MoveNext()
{
_current++;
return _current <= 3;
}

public void Reset()
{
_current = 0;
}
}

public IEnumerator<int> GetEnumerator()
{
return new OneToThree.OneToThreeEnumerator();
}

System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return new OneToThree.OneToThreeEnumerator();
}
}

class Program
{
static void Main(string[] args)
{
var onetothreeclass = new OneToThree();
var itor = onetothreeclass.GetEnumerator();
while (itor.MoveNext())
{
Console.WriteLine(itor.Current);
}
}
}
}

对照
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication1
{
class Program
{
static IEnumerable<int> GetOneToThreeClass()
{
int _current = 1;
while (_current <= 3)
{
yield return _current++;
}
}

static void Main(string[] args)
{
var onetothreeclass = GetOneToThreeClass();
var itor = onetothreeclass.GetEnumerator();
while (itor.MoveNext())
{
Console.WriteLine(itor.Current);
}
}
}
}
zhanglong19891129 2012-09-13
  • 打赏
  • 举报
回复
人了???
zhanglong19891129 2012-09-13
  • 打赏
  • 举报
回复
就是在疑问1处的时候他并不存储数据..
到疑问2 toarry()的时候.需要数组来保存的时候才去读数据处理数据.?这个是不是就是yield的延迟特性?
zhanglong19891129 2012-09-13
  • 打赏
  • 举报
回复

class Program
{
//这个方法会创建一个匿名迭代器类,等价之前的代码
static IEnumerable<int> GetOneToThreeClass()
{
int _current = 1;
while (_current <= 3)
{
yield return _current++;
}
}

static void Main(string[] args)
{
GetOneToThreeClass();//疑问1;
GetOneToThreeClass().ToArray();//疑问2
foreach (int x in GetOneToThreeClass())
{
Console.WriteLine(x);
}
}
}

我不明白的就是这个...
在疑问1 和疑问2 以及GetOneToThreeClass() 打好断点..
在执行疑问1的时候不会跟到GetOneToThreeClass()中去..
执行疑问2的时候就跟到GetOneToThreeClass()中去了..
这个有点想不通..这个应该就是和yield 有关的吧?
threenewbee 2012-09-13
  • 打赏
  • 举报
回复
贴一个手动实现迭代器的代码
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication1
{
class OneToThree : IEnumerable<int>
{
public class OneToThreeEnumerator : IEnumerator<int>, IEnumerator
{
private int _current = 0;

public int Current
{
get { return _current; }
}

public void Dispose()
{
//这里我们不需要清理
}

object System.Collections.IEnumerator.Current
{
get { return _current; }
}

public bool MoveNext()
{
_current++;
return _current <= 3;
}

public void Reset()
{
_current = 0;
}
}

public IEnumerator<int> GetEnumerator()
{
return new OneToThree.OneToThreeEnumerator();
}

System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return new OneToThree.OneToThreeEnumerator();
}
}

class Program
{
static void Main(string[] args)
{
foreach (int x in new OneToThree())
{
Console.WriteLine(x);
}
}
}
}

对照yield return
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication1
{
class Program
{
//这个方法会创建一个匿名迭代器类,等价之前的代码
static IEnumerable<int> GetOneToThreeClass()
{
int _current = 1;
while (_current <= 3)
{
yield return _current++;
}
}

static void Main(string[] args)
{
foreach (int x in GetOneToThreeClass())
{
Console.WriteLine(x);
}
}
}
}
zhanglong19891129 2012-09-13
  • 打赏
  • 举报
回复
[Quote=引用 1 楼 的回复:]

注意你的用词,O(n)和O(N)是不同的。
yield return本质上是一个状态机,C#会创建一个线程,并且每次迭代后都会将这个线程挂起,再次调用则继续。它向调用者返回当前迭代值,但是它不会保存到存储中,和List不同,也不能随机访问迭代器。
[/Quote]
可以这么理解么??实际上就是一小段内存,在foreach的时候,取当前值.假如foreach执行完了第一次了.这段内存就清空,然后在第二次的时候,把取到的值,放到内存中去??
zhanglong19891129 2012-09-13
  • 打赏
  • 举报
回复
[Quote=引用 4 楼 的回复:]

“那么时间复杂度是多少?100N?....” --- 1
不需要循环,直接取值,因为它有一个栈顶指针啊,任何时候操作都是在栈顶
[/Quote]
但是当前 栈顶执行完之后 必须的 drop()才行啊,,假如这个时间复杂度为 O(n),.递归一百次 时间复杂度为100 O(n)对不对?

110,502

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧