過了許久的時間,終於趁閒暇的時間來繼續將函數式編程這個專輯連載下去,這段時間開頭是為IOS這個新方向做准備,將OC的教程寫成了SWIFT版,當然我個人是支持Xamarin,但是我一般會先掌握原生態的開發,再掌握Xamarin。後面剩下的時間開發了一個Xamarin App項目,用了十幾天完成的。今天的內容將對比較簡單,就是講述如何利用函數式編程來實現列表推導。說的簡單點就是列表的數據並不像我們平時開發那樣實現寫好的,而是通過一定的算法計算出來的(不是程序一打開就計算完成的,而是在使用的時候才計算的,並且只計算到我們使用的那一項為止)。
用函數方法實現迭代器
首先我們當然是先編寫一個高階函數來實現列表推導的主要功能部分,具體的代碼如下所示:
public static IEnumerable<T> Sequence<T>(Func<T, T> getNext, T startVal, Func<T, bool> endReached)
{
if (getNext == null)
yield break;
yield return startVal;
T val = startVal;
while (endReached == null || !endReached(val))
{
val = getNext(val);
yield return val;
}
}
這個函數的作用就是根據一定的算法進行迭代,而這裡利用了C# 2.0才有的語法。該函數首先是根據startVal開始迭代,並且根據getNext計算下一個值,而endReached則用來約束結束條件。所以我們可以看到如果endReached為NULL也是可行的,這樣這個序列就是無窮的,當然不是死循環。下面我們利用一段簡單的代碼來掩飾如何使用這個函數:
static void Main(string[] args)
{
var oddNumbersForm1 = Sequence<int>(x => x + 2, 1, x => x >= 20);
foreach (int x in oddNumbersForm1)
{
Console.WriteLine(x);
}
Console.ReadKey();
}
最後我們在命令行界面中可以看到如下的顯示:
值域
很多時候我們使用的序列都是很簡單的,而利用上面的Sqquence函數則會變得復雜,同時這個函數還無法實現一些我們需要的功能,比如需要判斷一個值時候存在范圍中等等,所以我們下面將利用一個簡單的Range來實現這些功能,當然內部還是使用的如上的Sequence函數,下面是具體的代碼:
public class Range<T> : IEnumerable<T>
{
private T start;
private T end;
private Comparison<T> compare;
private IEnumerable<T> sequence;
private static int Compare<U>(U one,U other)
{
return Comparer<U>.Default.Compare(one,other);
}
public Range(T start, T end, Func<T, T> getNext, Comparison<T> compare)
{
this.start = start;
this.end = end;
this.compare = compare;
this.sequence = Sequence<T>(getNext, start, v => compare(getNext(v), end) > 0);
}
public Range(T start, T end, Func<T, T> getNext)
: this(start, end, getNext, Compare) { }
public IEnumerator<T> GetEnumerator()
{
return sequence.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return ((IEnumerable<T>)this).GetEnumerator();
}
public bool Contains(T value)
{
return compare(value, start) >= 0 && compare(end, value) >= 0;
}
}
通過上面的代碼我們可以看到Range的功能遠遠比Sequence函數要強大的多,並且我們可以看到我們還提供了Contains方法用來判斷指定的值是否在值域中,另外我們還可以獲得IEnumerable借口的各種擴展方法供我們使用。
限制
有時我們的迭代器的實現可能很復雜,如果還要加上限制不僅僅會影響到其性能,同時還會導致代碼的可讀性較差,所以我們可以將這兩者進行分離,負責生成序列的函數只需要沒有任何限制,而將限制移轉到外部,比如函數TFunc是一個返回序列的函數,並且沒有限制,而CFunc則接受一個最大值,以及用來計算序列的函數,並且返回負責條件的序列,那麼我們就可以像如下這樣循環獲取值:
foreach(int x in CFunc(10,TFunc)){
Console.WriteLine(x);
}
至此關於函數式編程中的序列就結束了。