Loading

ARTS_2

概述

ARTS 是耗子叔发起的编程挑战:

每周完成一个ARTS: 每周至少做一个 leetcode 的算法题、阅读并点评至少一篇英文技术文章、学习至少一个技术技巧、分享一篇有观点和思考的技术文章。(也就是 Algorithm、Review、Tip、Share 简称ARTS)

Algorithm

https://leetcode.cn/problems/missing-two-lcci/

浅浅挑了一道简单hard。跟之前有道找到数组中消失的一个数字很像。感觉就是套用做法。

My Solution:

func missingTwo(nums []int) []int {
	nums = append(nums, -1, -1)
	for i := range nums {
		for {
			if nums[i] == -1 || nums[i] == i+1 {
				break
			}
			nums[i], nums[nums[i]-1] = nums[nums[i]-1], nums[i]
		}
	}
	res := make([]int, 0, 2)
	for i := range nums {
		if nums[i] == -1 {
			res = append(res, i+1)
		}
	}
	return res
}

go还是挺快的,我C++就打败了20%,go就90%。不过空间因为我没有预先分配,所以就打败了40%。

在评论区看到了两种其他做法。

第一种:

  1. 算出\(1...N\)的和,之后减去这个数组的和,你就获得了\(a+b\)的值

  2. 算出\(1^2...N^2\)的和,之后减去这个数组的平方和,你就获得了\(a^2 + b^2\)

  3. 之后就是联立得到一元二次方程:

    \((a + b) \pm \sqrt{(2(a ^ 2 + b ^ 2) - 2 (a+b)^2))}\)

    解出来就是a,b的值,记得取绝对值。

ps:不过这个做法有点问题就是要是暴力先算平方和的话,会爆int,要是边加边减应该没问题。

(纪念一下终于用上了latex🥵)

第二种:

  1. 算出\(1...N\)\(xor\)和,之后\(xor\)去这个数组的和,你就获得了\(a \oplus b\)的值

  2. 算出\(a \oplus b\)的lowbit,将1-n分成了两类,一类是跟lowbit的&有值的,另外一个是没有值的。

  3. 如法炮制nums数组即可。

func lowbit(x int) int {
	return x & -x
}

func missingTwo(nums []int) []int {
	var AxorB int
	for i := 1; i <= len(nums)+2; i++ {
		AxorB ^= i
	}

	for i := range nums {
		AxorB ^= nums[i]

	}
	var A int
	for i := 1; i <= len(nums)+2; i++ {
		if lowbit(AxorB)&i != 0 {
			A ^= i
		}
	}
	for i := range nums {
		if lowbit(AxorB)&nums[i] != 0 {
			A ^= nums[i]
		}
	}
	return []int{AxorB ^ A, A}
}

Tips

最近看代码看到了用api控制测试服务器流程的,本质是通过http发送一个带有js脚本的request给服务器,之后服务器会根据js脚本调用对应注册的函数。

主要学习怎么使用Go和JavaScript联动:

https://blog.csdn.net/wyongqing/article/details/124704136

http://www.codebaoku.com/bcalg/bcalg-goja.html

最主要的貌似是因为有goja这个库的存在,貌似并没有特别完善。不过已经可以支持ES5.1的全部功能,以及部分ES6的功能。

goja.New()最主要的是因为有这个函数,这个函数返回一个goja.Runtime的对象。这个对象实现了一个js虚拟机。之后就可以把各种代码和操作放在这个对象上去执行。Go与JS进行交互也是在这个对象上面。

ps: 使用前记得要在import里面写这个库之后go mod tidy

比如博文里提到的:

Go获得JS表达式的值

func testExpression(){
	//创建运行实例
	vm := goja.New()
    //运行表达式 
	v, _ := vm.RunString("2 + 2")
	//获得结果
	num := v.Export().(int64)
	//打印结果
	fmt.Println(num)
}

Go获得JS变量的值

func testParamGetFromJs(){
	vm := goja.New()
	vm.RunString(`
	var num = 4;
`)

	fmt.Println(vm.Get("num").Export().(int64))
}

JS调用Go的函数

func Test(a int, b int) int{
	return a+b
}

func testCallGoFunc(){
	vm := goja.New()

	vm.Set("Test", Test)
	v, _ := vm.RunString(`
	Test(2, 2);
`)
	fmt.Println(v.Export().(int64))
}

还有一些用法在上面的博客里有就不在举例。不过我发现JS代码里会有var net = require('net')我很好奇,这个JS如果跟Go结合起来要怎么用。

最后发现这部分其实是node.js的语法。require就是把一个js文件当作一个对象来导入,之后它里面的方法就都可以通过这个对象来调用了。

但是在Goja里面,它实现了goja_nodejs/require,也就是说你可以把go的对象构造func或者是一些通用func注册到js的xxx的module里面,通过js的var req = require('xxx')去调用注册进去的函数。

Review

https://medium.com/towardsdev/when-and-where-to-use-pointers-in-go-7e89643a2c9

https://learnku.com/go/t/60923

这两篇文章讲的是golang中什么时候使用指针比较好。其实这几个月的golang使用下来,我个人对什么时候用指针大部分还是有点感觉,但是没有特别有条理。并且我对这个对于传入参数拷贝消耗较大时,使用指针中的这个较大不是很清楚。

于是就来系统性学习一下。

这篇文章主要是通过指针的Dos和Don'ts来让读者感受什么时候要用指针,什么时候不用。

Dos


Use the pointers when modifying the internal data or state of the method receiver.

在需要修改receiver的内部数据的时候,请使用指针。

image-20221008103743889

就拿上面的图片举例,也就是说,只有指针类型的receiver才能修改内部。(报错的原因是因为同名了,因为*A与A都被视为A的方法)


Use the pointer type parameter when modifying the parameter value or the internal data.

在需要修改参数值或者参数内部值的时候使用指针,这个就不再赘述。

The pointer type is needed when the struct is required as the slice and map value types and its variable needs direct modification later. Otherwise, you can only copy and create a new object to replace it, taking the possible performance risks into consideration.

当结构体内部有slice或者map等引用类型的时候,请使用指针。

因为slice和map是引用类型,当拷贝了之后,struct内部的slice或map还是会指向原来的地方。

Don’ts


Do not use pointers for reference types like map, slice, channel.

请不要对引用类型使用指针。

img

因为引用类型的内部实现就是指针了,所以不需要再额外添加一层指针。


Avoid using pointers if concurrency safety is required. Keep in mind that it is an intrusive way to modify variables using pointers, and sometimes data safety is first priority.

请避免使用指针当有并发的时候。

这个也比较好理解,并发访问指针之后并发修改数据会导致脏数据,数据的准确性才是我们最关心的。


Try not to nest pointers like **string, which will make your code extremely complex, although it is allowed by Go.

请不要使用多重指针

这会增加代码的debug难度,同时减少代码的可读性,难以维护。

不过这篇英文文章还是没有提到什么时候,才是较大的时候。于是我就去百度了一下,第二篇文章告诉我,要根据实际情况进行压测,这样就知道系统的消耗到底在哪,以及使用指针到底能不能优化,是正优化还是负优化。

Share

https://zhuanlan.zhihu.com/p/72896309

https://www.daodejing.org/77.html

好奇怪的鸡汤,讲的是熵增定律。说我们整个宇宙都是熵增的过程,只有引入外力才能逆熵。

emm,虽然讲的很好,但是好像并没有鸡到我。可能是因为我本来就烦恼比较少吧,而且也在好好地生活。

不过评论里面讲了道德经,我就去看了一下。

”天之道损有余而补不足,人之道则不然,损不足以奉有余“,老子在千年前讲的东西,放在当今社会仍然是一样的。也许这就是人类社会的必然走向吧。

只希望自己不会被"损",同时也不希望自己被"奉",在中间地带有着自己的小小幸福就行。

不过这个的评论,放在这个文章下面,是有点不对嘴的。感觉知乎评论水平还有待提高啊。

posted @ 2022-10-08 11:17  ViKyanite  阅读(31)  评论(0编辑  收藏  举报