服务计算 - 修改、改进 RxGo 包

ReactiveX

Reactive Extensions的缩写,一般简写为Rx,最初是LINQ的一个扩展,由微软的架构师Erik Meijer领导的团队开发,在2012年11月开源,Rx是一个编程模型,目标是提供一致的编程接口,帮助开发者更方便的处理异步数据流,Rx库支持.NET、JavaScript和C++,Rx近几年越来越流行了,现在已经支持几乎全部的流行编程语言了,Rx的大部分语言库由ReactiveX这个组织负责维护,比较流行的有RxJava/RxJS/Rx.NET

尝试使用pmlpml/RxGo

github链接
照着readme尝试进行使用

hello world

package main

import (
	"fmt"

	RxGo "github.com/pmlpml/rxgo"
)

func main() {
	RxGo.Just("Hello", "World", "!").Subscribe(func(x string) {
		fmt.Println(x)
	})
}

hello world结果如图
在这里插入图片描述

链式

package main

import (
	"fmt"
	RxGo "github.com/pmlpml/rxgo"
)

func fibonacci(max int) func() (int, bool) {
	a, b := 0, 1
	return func() (r int, end bool) {
		r = a
		a, b = b, a+b
		if r > max {
			end = true
		}
		return
	}
}

func main() {
	RxGo.Start(fibonacci(10)).Map(func(x int) int {
		return 2*x
	}).Subscribe(func(x int) {
		fmt.Print(x)
	})
}

链式测试,此次测试先用start定了个0112358的源observable,随后用Map对对象内的数全进行了修改,最后用Subscribe打印出
在这里插入图片描述

连接

package main

import (
	"fmt"

	RxGo "github.com/pmlpml/rxgo"
)

func main() {
	//define pipeline
	source := RxGo.Just("Hello", "World", "!")
	next := source.Map(func(s string) string {
		return s + " "
	})
	//run pipeline
	next.Subscribe(func(x string) {
		fmt.Print(x)
	})
	fmt.Println()
	source.Subscribe(func(x string) {
		fmt.Print(x)
	})
}

这个主要测试是否是深修改,结果如下
在这里插入图片描述

实验部份,实现Filtering

实验过程

首先要知道实现方法,参照老师已经实现了的filter,观察他的调用函数顺序
Start()只是创建了个observable,并赋予了它的基础属性,直到Subscribe()上才是最终启动
Subscribe()中又动用到了connect(), connect()又动用到了op,这个op接口才是自己需要实现的

新开了个filterings.go之后就开始实现以下函数,
首先把op跟transOperator直接搬过来,改下名字跟类型

type filteringsOperater struct {
	opFunc func(ctx context.Context, o *Observable, item reflect.Value, out chan interface{}) (end bool)
}

func (tsop filteringsOperater) op(ctx context.Context, o *Observable) {
	// must hold defintion of flow resourcs here, such as chan etc., that is allocated when connected
	// this resurces may be changed when operation routine is running.
	in := o.pred.outflow
	out := o.outflow
	//fmt.Println(o.name, "operator in/out chan ", in, out)
	var wg sync.WaitGroup

	go func() {
		end := false
		for x := range in {
			if end {
				continue
			}
			// can not pass a interface as parameter (pointer) to gorountion for it may change its value outside!
			xv := reflect.ValueOf(x)
			// send an error to stream if the flip not accept error
			if e, ok := x.(error); ok && !o.flip_accept_error {
				o.sendToFlow(ctx, e, out)
				continue
			}
			// scheduler
			switch threading := o.threading; threading {
			case ThreadingDefault:
				if tsop.opFunc(ctx, o, xv, out) {
					end = true
				}
			case ThreadingIO:
				fallthrough
			case ThreadingComputing:
				wg.Add(1)
				go func() {
					defer wg.Done()
					if tsop.opFunc(ctx, o, xv, out) {
						end = true
					}
				}()
			default:
			}
		}

		wg.Wait() //waiting all go-routines completed
		o.closeFlow(out)
	}()
}

之后开始尝试实现下面的功能,出现问题了在对上面做改动
先装上 “github.com/stretchr/testify/assert”,“github.com/davecgh/go-spew/spew”,“github.com/pmezard/go-difflib/difflib”
再写这十二个功能的测试

package rxgo_test

import (
	"testing"
	"time"

	"github.com/pmlpml/rxgo"
	"github.com/stretchr/testify/assert"
)

func TestDebounce(t *testing.T) {
	res := []int{}
	ob := rxgo.Just(1, 2, 3, 4, 5, 6, 7, 8, 9).Map(func(x int) int {
		if x == 2 || x == 5 {
			time.Sleep(3 * time.Millisecond)
		} else {
			time.Sleep(1 * time.Millisecond)
		}
		return x
	}).Debounce(2 * time.Millisecond)

	ob.Subscribe(func(x int) {
		res = append(res, x)
	})

	assert.Equal(t, []int{1, 4, 9}, res, "debounce error")
}

func TestDistinct(t *testing.T) {
	res := []int{}
	ob := rxgo.Just(1, 2, 3, 3, 4, 5, 6, 7, 8, 9).Map(func(x int) int {
		time.Sleep(1 * time.Millisecond)
		return x
	}).Distinct()

	ob.Subscribe(func(x int) {
		res = append(res, x)
	})

	assert.Equal(t, []int{1, 2, 3, 4, 5, 6, 7, 8, 9}, res, "distinct error")
}

func TestElementAt(t *testing.T) {
	res := []int{}
	ob := rxgo.Just(1, 2, 3, 4, 5, 6, 7, 8, 9).Map(func(x int) int {
		time.Sleep(1 * time.Millisecond)
		return x
	}).ElementAt(1)

	ob.Subscribe(func(x int) {
		res = append(res, x)
	})

	assert.Equal(t, []int{2}, res, "elementAt error")
}

func TestFirst(t *testing.T) {
	res := []int{}
	ob := rxgo.Just(1, 2, 3, 4, 5, 6, 7, 8, 9).Map(func(x int) int {
		time.Sleep(1 * time.Millisecond)
		return x
	}).First()

	ob.Subscribe(func(x int) {
		res = append(res, x)
	})

	assert.Equal(t, []int{1}, res, "first error")
}

func TestIgnoreElements(t *testing.T) {
	res := []int{}
	ob := rxgo.Just(1, 2, 3, 4, 5, 6, 7, 8, 9).IgnoreElements()

	ob.Subscribe(func(x int) {
		res = append(res, x)
	})

	assert.Equal(t, []int{}, res, "ignore element error")
}

func TestLast(t *testing.T) {
	res := []int{}
	ob := rxgo.Just(1, 2, 3, 4, 5, 6, 7, 8, 9).Last()

	ob.Subscribe(func(x int) {
		res = append(res, x)
	})

	assert.Equal(t, []int{9}, res, "last error")
}

func TestSkip(t *testing.T) {
	res := []int{}
	ob := rxgo.Just(1, 2, 3, 4, 5, 6, 7, 8, 9).Map(func(x int) int {
		time.Sleep(1 * time.Millisecond)
		return x
	}).Skip(3)

	ob.Subscribe(func(x int) {
		res = append(res, x)
	})

	assert.Equal(t, []int{4, 5, 6, 7, 8, 9}, res, "skip error")
}

func TestSkiplast(t *testing.T) {
	res := []int{}
	ob := rxgo.Just(1, 2, 3, 4, 5, 6, 7, 8, 9).Map(func(x int) int {
		time.Sleep(1 * time.Millisecond)
		return x
	}).Skiplast(6)

	ob.Subscribe(func(x int) {
		res = append(res, x)
	})

	assert.Equal(t, []int{1, 2, 3}, res, "skiplast error")
}

func TestTake(t *testing.T) {
	res := []int{}
	ob := rxgo.Just(1, 2, 3, 4, 5, 6, 7, 8, 9).Map(func(x int) int {
		time.Sleep(1 * time.Millisecond)
		return x
	}).Take(3)

	ob.Subscribe(func(x int) {
		res = append(res, x)
	})

	assert.Equal(t, []int{1, 2, 3}, res, "take error")
}

func TestTakelast(t *testing.T) {
	res := []int{}
	ob := rxgo.Just(1, 2, 3, 4, 5, 6, 7, 8, 9).Map(func(x int) int {
		time.Sleep(1 * time.Millisecond)
		return x
	}).Takelast(3)

	ob.Subscribe(func(x int) {
		res = append(res, x)
	})

	assert.Equal(t, []int{7, 8, 9}, res, "takelast error")
}


写完测试后就是单元实现,实现就不放具体代码了,简要说明一下每个函数在干啥就是了

  1. Debounce
    Debounce的参数代表从后往前数间隔时间需大于参数才会被筛选出来,主要作用是过滤发射数据过快的项
    单元测试结果
    在这里插入图片描述

  2. Distinct
    去掉重复的项
    在这里插入图片描述
    这里着重讲一下,本来在测试的时后是没有加map(给每个项延迟避免并发),可能是我实现的时后没有加互斥锁,导致每次测试顺序都不对,避免麻烦就在测试的时后加了延迟。(码力不足见量)

  3. ElementAt
    返回在该下标的项
    在这里插入图片描述

  4. Filter
    filter老师已经实现了就不重复做了

  5. First
    返回第一项
    在这里插入图片描述

  6. IgnoreElements
    啥都不返回
    在这里插入图片描述

  7. Last
    返回最后一个,由于不知道输入流什么时候结束,所以每输入进一个就存一个,直到没东西为止才输出。
    由于使用了filter做判断,所以对op做了简单修改

    加了这个

    	if o.flip != nil {
    		buffer, ok := reflect.ValueOf(o.flip).Interface().([]reflect.Value)
    		if !ok {
    			panic("filter.go(op): buffer is not a slice")
    		}
    		for _, v := range buffer{
    			o.sendToFlow(ctx, v.Interface(), out)
    		}
    	}
    

    在这里插入图片描述

  8. Skip
    跳过前n项
    在这里插入图片描述

  9. SkipLast
    跳过后n项,由于不知道尽头在哪,所以弄了个n大小的队列,即可简单实现
    在这里插入图片描述

  10. Take
    拿前n项
    在这里插入图片描述

  11. TakeLast
    拿后n项
    在这里插入图片描述

项目代码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值