Giter Club home page Giter Club logo

interview-go's Issues

建议多增加以下方面的内容

过往经验来看
企业面试gopher的时候
比较高频的问题
大体还是集中在
goroutine,channel,gRPC,gin方面
希望可以多增加这方面的内容

golang case 语句

golang case 语句出现在三个地方

条件判断中的 switch...case

var a = "hello"
switch a {
case "hello":
    fmt.Println(1)
case "world":
    fmt.Println(2)
default:
    fmt.Println(0)
}

一分支多值

var a = "mum"
switch a {
case "mum", "daddy":
    fmt.Println("family")
}

分支表达式

var r int = 11
switch {
case r > 10 && r < 20:
    fmt.Println(r)
}

此时switch 没有变量和表达式,case 各分支默认和 true 比较
通过 fallthrough 继续执行其他case

var s = "hello"
switch {
case s == "hello":
    fmt.Println("hello")
    fallthrough
case s != "world":
    fmt.Println("world")
}

接口类型断言中的 switch...case

这种情况其实属于上种情况,只是和接口类型断言关联,比较常见,特拿出来单说

switch v := i.(type)  {
case int:
    ...
case string:
    ...
default:
    ...
}

关键字type只能用在switch语句里,如果用在switch外面会报错

并发模型中的 select...case

select {
case v1 := <-c1:
    fmt.Printf("received %v from c1\n", v1)
case v2 := <-c2:
    fmt.Printf("received %v from c2\n", v1)
case c3 <- 23:
    fmt.Printf("sent %v to c3\n", 23)
default:
    fmt.Printf("no one was ready to communicate\n")
}
  1. 除 default 外,如果只有一个 case 语句评估通过,那么就执行这个case里的语句;

  2. 除 default 外,如果有多个 case 语句评估通过,那么通过伪随机的方式随机选一个;

  3. 如果 default 外的 case 语句都没有通过评估,那么执行 default 里的语句;

  4. 如果没有 default,那么 代码块会被阻塞,指导有一个 case 通过评估;否则一直阻塞

“实现阻塞读且并发安全的map”答案补充

我注意到https://github.com/lifei6671/interview-go/blob/master/question/q010.md中缺少了Rdfunc的实现,我完成了,并且经过了简单的测试。

func (m *Map) Rd(key string, timeout time.Duration) interface{} {
	e, ok := m.c[key]
	if !ok {
		// create channel
		m.c[key] = &entry{
			isExist: false,
			ch:      make(chan struct{}),
		}
	}
	// 阻塞
	e, _ = m.c[key]
	if e.ch != nil {
		select {
		case <-e.ch:
		case <-time.After(timeout):
		}
	}
	return m.c[key].value
}

实现 Sunday 匹配 精简版代码

https://github.com/lifei6671/interview-go/blob/master/algorithm/docs/match-sunday-string.md

精简版代码供参考:

func strStrSunday(haystack, needle string) int {
	if haystack == "" {
		return -1
	}
	if needle == "" {
		return 0
	}

	haystackLen := len(haystack)
	needleLen := len(needle)
	i := 0
	for {
		// 先判断剩余字符串的长度是否足够
		if i+needleLen > haystackLen {
			return -1
		}
		// 匹配上之后,直接返回index
		if haystack[i:i+needleLen] == needle {
			return i
		}
		// 判断needle所在位置的下一个占位是否包含在haystack中
		// needle所在位置的下一个占位可能超出haystack的长度
		if i+needleLen == haystackLen {
			return -1
		}
		//lastIdx := strings.LastIndex(needle, string(haystack[i+needleLen]))
		lastIdx := -1
		for j := needleLen - 1; j >= 0; j-- {
			if haystack[i+needleLen] == needle[j] {
				lastIdx = j
				break
			}
		}
		// 根据lastIdx结果进行不同的移位
		if lastIdx >= 0 {
			i += needleLen - lastIdx
		} else {
			i += needleLen + 1
		}
	}
}

interview-go/question /q015.md

  1. Pool 内存不会暴涨。
    内存主要受256M协程影响。 定时501 ms 增长一次。因为它无法抢到pool中的buffer, 所以每次都要grow一份。
    估计最多能grow 到 256M *1001.
    但是存在gc调用,不知道gc会如何释放pool中的内存?
    把256M改为 1M的时候,发现内存会波动,没理解原因

交替打印数字和字母 - 示例问题描述及优化

示例代码:

	letter,number := make(chan bool),make(chan bool)
	wait := sync.WaitGroup{}

	go func() {
		i := 1
		for {
			select {
			case <-number:
				fmt.Print(i)
				i++
				fmt.Print(i)
				i++
				letter <- true
				break
			default:
				break
			}
		}
	}()
	wait.Add(1)
	go func(wait *sync.WaitGroup) {
		str := "ABCDEFGHIJKLMNOPQRSTUVWXYZ"

		i := 0
		for{
			select {
			case <-letter:
				if i >= strings.Count(str,"")-1 {
					wait.Done()
					return
				}

				fmt.Print(str[i:i+1])
				i++
				if i >= strings.Count(str,"") {
					i = 0
				}
				fmt.Print(str[i:i+1])
				i++
				number <- true
				break
			default:
				break
			}

		}
	}(&wait)
	number<-true
	wait.Wait()

这个代码正确得到了结果,但是代码它本身存在严重的瑕疵:

  • (重要)接收信号使用了 select default 模式, chan 没有数据时,陷入多余的循环,浪费硬件资源
  • (重要)没有正确关闭 number 与 letter 通道,这个简短的程序它运行没有问题,是等待 main 函数退出系统回收相关资源的
  • (重要)打印 number 的 goroutine 没有正确关闭,是等待 main 函数退出系统回收
  • (次要)低效率的字典模式打印字符串,需要预先创建需要打印字符的字典,然后按字典进行打印

一些建议

  1. 尽量给出使用标准库的解法,与纯算法(不使用任何库)两种。使用库将题做对,面试官可能也不会特别满意,虽然也足以应付一般的场景。
  2. 给出每个解法的时间复杂度,空间复杂度。如果能够分析为什么是这样最好。示例解法由于使用了大量标准库方法,效率太低了,除非你的代码永远没有优化的需求,否则你需要理解如何优化时间复杂度或空间复杂度。

『goroutine和channel使用一』 答案协程泄露

原题: https://github.com/lifei6671/interview-go/blob/master/question/q009.md
答案中done <- true 后面应该加上 return,否则子协程不会立刻退出, 往已经关闭的 done 写数据
如果此后做一些处理的话就会触发 panic

当函数被循环调用时, 必现 panic: send on closed channel

func main() {
	random := make(chan int)
	done := make(chan bool)

	go func() {
		for {
			num, ok := <-random
			if ok {
				fmt.Println(num)
			} else {
				done <- true
			}
		}
	}()

	go func() {
		defer close(random)

		for i := 0; i < 5; i++ {
			random <- rand.Intn(5)
		}
	}()

	<-done
	close(done)
        // 触发 panic
        time.Sleep(1 * time.Second)
}

实现 Sunday 匹配 needle在最末尾的时候返回结果异常

s := "hello world abc is a test chars in program develop comm"
index := strStrSunday(s, "common")
fmt.Println("common index is", index)
正常应该返回-1, 实际会返回54 什么的

附上根据原链接java 版本实现的一个方法:

`
func strStrSunday(haystack string, needle string) int {
if len(haystack) < len(needle) {
return -1
}

  if haystack == needle {
	  return 0
  }

  originIndex := 0
  aimIndex := 0
  nextIndex := 0

  for aimIndex < len(needle) {
	  if originIndex > len(haystack)-1 {
		  return -1
	  }

	  if haystack[originIndex] == needle[aimIndex] {
		  originIndex++
		  aimIndex++
	  } else {
		  nextIndex = originIndex - aimIndex + len(needle)
		  if nextIndex < len(haystack) {
			  offset := strings.LastIndex(needle, string(haystack[nextIndex]))
			  if offset == -1 {
				  originIndex = nextIndex + 1
			  } else {
				  originIndex = nextIndex - offset
			  }

			  aimIndex = 0
		  } else {
			  return -1
		  }
	  }
  }

  return originIndex - aimIndex

}
`

实现阻塞读的并发安全Map

package main

import (
	"fmt"
	"time"
	"sync"
)

type sp interface {
	Out(key string, val interface{})  //存入key /val,如果该key读取的goroutine挂起,则唤醒。此方法不会阻塞,时刻都可以立即执行并返回
	Rd(key string, timeout time.Duration) interface{}  //读取一个key,如果key不存在阻塞,等待key存在或者超时
}


type Item2 struct {
	value interface{}
	ch    chan struct{}
	exist bool
}
type ConcurrentMap struct {
	mapval map[string]*Item2
	rwmutex  *sync.RWMutex
}

func NewConcurrentMap() *ConcurrentMap {
	return &ConcurrentMap{
		mapval: make(map[string]*Item2, 0),
		rwmutex:  new(sync.RWMutex),
	}
}

func (c *ConcurrentMap) Out(key string, val interface{}) {
	c.rwmutex.Lock()
	defer c.rwmutex.Unlock()

	item, ok := c.mapval[key]
	if !ok {
		c.mapval[key] = &Item2{
			value: val,
			exist: true,
		}
		return
	}

	item.value = val
	if !item.exist {
		if item.ch != nil {
			close(item.ch)
			item.ch = nil
		}
	}

	return
}

func (c *ConcurrentMap) Rd(key string, timeout time.Duration) interface{} {
	c.rwmutex.Lock()
	defer c.rwmutex.Unlock()

	item, ok := c.mapval[key]
	if !ok {
		item = &Item2{
			exist: false,
			ch: make(chan struct{}, 1),
		}
		c.mapval[key] = item
	}

	if item.exist {
		return item.value
	}

	select {
	case <-time.After(timeout):
		return nil
	item, _ = c.mapval[key]
	return item.value
}


func main() {
	mapval := NewConcurrentMap()
	mapval.Out("yanglei", "xx")
	mapval.Out("yanglei", "yy")

	fmt.Printf("%v\n", mapval.Rd("yanglei2", 5 * time.Second))
}

冒泡排序完全是错的

冒泡排序是通过数去找位置,这里的冒泡排序写成选择排序了,附代码

func bubbleSort(arr []int) []int {
	if len(arr) == 0 {
		return arr
	}
	n := len(arr)
	for i := 0; i < n-1; i++ {
		for j := 0; j < n-1-i; j++ {
			if arr[j] > arr[j+1] {
				arr[j], arr[j+1] = arr[j+1], arr[j]
			}
		}
	}
	return arr
}

Goroutine泄露问题——交替打印数字和字母

Goroutine是如何泄露的

在交替打印数字和字母的解法里面,负责打印字母的goroutine可以正常退出,但是负责打印数字的goroutine是一个死循环,因此示例代码存在泄露goroutine的风险

	go func() {
		i := 1
		for {
			select {
			case <-number:
				fmt.Print(i)
				i++
				fmt.Print(i)
				i++
				letter <- true
			}
		}
	}()

解决办法

负责打印字母的goroutine在退出时可以执行close(number)告知打印数字的goroutine退出

package main

import (
	"fmt"
	"sync"
)

func main() {
	letter, number := make(chan bool), make(chan bool)
	wg := sync.WaitGroup{}

	wg.Add(1)
	go func() {
		defer func() {
			fmt.Println("\nNumber goroutine exit.")
			wg.Done()
		}()
		i := 1
		for range number {
			fmt.Print(i)
			i++
			fmt.Print(i)
			letter <- true
		}
	}()

	wg.Add(1)
	go func(wg *sync.WaitGroup) {
		defer func() {
			close(number)
			fmt.Println("\nLetter goroutine exit.")
			wg.Done()
		}()
		i := 'A'
		for range letter {
			if i >= 'Z' {
				return
			}

			fmt.Print(string(i))
			i++
			fmt.Print(string(i))
			i++
			number <- true
		}
	}(&wg)

	number <- true
	wg.Wait()
}

翻转字符串

这个题目,不发生内存拷贝?? 讲道理, 字符串数组转换成[]rune切片, 就已经发生了内存拷贝了好吧

第七题的第七小题解答不够到位

https://github.com/lifei6671/interview-go/blob/master/question/q007.md#7%E4%B8%8B%E9%9D%A2%E7%9A%84%E7%A8%8B%E5%BA%8F%E8%BF%90%E8%A1%8C%E5%90%8E%E4%B8%BA%E4%BB%80%E4%B9%88%E4%BC%9A%E7%88%86%E5%BC%82%E5%B8%B8

func (p *Project) run(msgchan chan interface{}) {
	for {
		defer p.deferError()
		go p.exec(msgchan)
		time.Sleep(time.Second * 2)
	}
}

这一段错误不仅是 defer p.deferError() 没有出现在协程内部,而是无节制地在创建协程。虽然通过 time.Sleep(time.Second * 2) 限制了频率,但如果以 Main 内 time.Sleep(time.Second * 100000000000000) 如此久的等待,迟早也会因为内存占用过大而报错。

question17/多协程切片查询

有以下的两种情况,需要大家思考一下:
1、在规定的时间内,如果没找到,这种情况怎么处理,当前的处理方式是等到超时退出。
2、关于size的设定。需要根据具体情况跑benchmark,来确定最佳的性能。(同时也需要注意size太小的话,goroutine数量会太多)

最长公共前缀 go版本代码有误

给出的实例代码中 内循环没有判断是否超过字符串长度,如果给出[]string{"flower", "flow", "flow"} 这样的输入直接就panic了。
建议修改为

for i := range firstStr {
		for j := 1; j < l; j++ {
			if len(arr[j]) == i || arr[j][i] != firstStr[i] {
				return firstStr[:i]
			}
		}
	}

q014最后一题的解释不通啊

type Student struct {
Age int
}

func main() {
kv := map[string]*Student{"a":{Age:21}}
kv["a"].Age = 22
fmt.Println(kv)
}
这样修改为啥不报错了呢

q015 “双检查实现单例”问题

答案是:在多核CPU中,因为CPU缓存会导致多个核心中变量值不同步。
我的观点是:当前核心的变量值发生改变时,其他核心缓存中的值会置为不可用,当读取或修改时会重新从主存里读取。所以应该不会有不同步的问题。并且我也无法浮现这个问题 求解

第二题中的一些疑问?

既然都是ASCII码,那长度大于256必定会出现重复的,所以改为以下是不是可以:

func isUniqueString(s string) bool {
	if len(s) > 256 {
		return false
	}

	unique := make(map[int32]bool)

	for _, v := range s {
		if v > 256 {
			return false
		}
		if _, ok := unique[v]; ok {
			return false
		} else {
			unique[v] = true
		}
	}
	return true
}

实现阻塞读的并发安全Map

package main

import (
"fmt"
"time"
"sync"
)

type sp interface {
Out(key string, val interface{}) //存入key /val,如果该key读取的goroutine挂起,则唤醒。此方法不会阻塞,时刻都可以立即执行并返回
Rd(key string, timeout time.Duration) interface{} //读取一个key,如果key不存在阻塞,等待key存在或者超时
}

type Item2 struct {
value interface{}
ch chan struct{}
exist bool
}
type ConcurrentMap struct {
mapval map[string]*Item2
rwmutex *sync.RWMutex
}

func NewConcurrentMap() *ConcurrentMap {
return &ConcurrentMap{
mapval: make(map[string]*Item2, 0),
rwmutex: new(sync.RWMutex),
}
}

func (c *ConcurrentMap) Out(key string, val interface{}) {
c.rwmutex.Lock()
defer c.rwmutex.Unlock()

item, ok := c.mapval[key]
if !ok {
	c.mapval[key] = &Item2{
		value: val,
		exist: true,
	}
	return
}

item.value = val
if !item.exist {
	if item.ch != nil {
		close(item.ch)
		item.ch = nil
	}
}

return

}

func (c *ConcurrentMap) Rd(key string, timeout time.Duration) interface{} {
c.rwmutex.Lock()
defer c.rwmutex.Unlock()

item, ok := c.mapval[key]
if !ok {
	item = &Item2{
		exist: false,
		ch: make(chan struct{}, 1),
	}
	c.mapval[key] = item
}

if item.exist {
	return item.value
}

select {
case <-time.After(timeout):
	return nil
case <-item.ch:
}

item, _ = c.mapval[key]
return item.value

}

第六题机器人坐标计算有问题

第六题机器人坐标计算
如果命令字符串包含嵌套的命令,那么题中的代码就会有问题。
举个例子,输入的命令为2(F3(B)),正确的答案是(0,-4)。然而给出的代码输出是(0,0)
正确的代码可以参考如下:

const (
	NORTH = iota
	EAST
	SOUTH
	WEST
)

type Offset struct {
	x int
	y int
}

var dirlist []Offset
var dirname []string

func init() {
	dirname = []string{"NORTH", "EAST", "SOUTH", "WEST"}
	dirlist = []Offset{{0, 1}, {1, 0}, {0, -1}, {-1, 0}}
}

func getOffset(offset Offset, sign int, x int, y int) (int, int) {
	return x + offset.x*sign, y + offset.y*sign
}

func rmove(dir int, x int, y int, start int, s string) (int, int, int, int) {

	times := 0

	i := start
	//fmt.Printf("start:%d\n", start)
	for ; i < len(s); i++ {
		ch := s[i]
		//fmt.Printf("ch:%d,", ch)
		if ch >= '0' && ch <= '9' {
			times = times*10 + int(ch-'0')
		} else if ch == '(' {
			next := 0
			for j := 0; j < times; j++ {
				x, y, dir, next = rmove(dir, x, y, i+1, s)
			}
			i = next
		} else if ch == ')' {
			break
		} else if ch == 'R' {
			dir = (dir + 1) % 4
		} else if ch == 'L' {
			dir = (dir + 3) % 4

		} else if ch == 'F' {
			x, y = getOffset(dirlist[dir], 1, x, y)

		} else if ch == 'B' {
			x, y = getOffset(dirlist[dir], -1, x, y)
		}
	}
	//fmt.Printf("s:%s,x:%d,y:%d,dir:%d,i:%d\n", s, x, y, dir, i)
	return x, y, dir, i
}

// @返回参数1: 作标
// @返回参数2:方向
func move(s string) ([]int, int) {

	dir := NORTH
	x, y, dir, _ := rmove(dir, 0, 0, 0, s)
	//fmt.Printf("dir:%s\n", dirname[dir])
	return []int{x, y}, dir
}

q007常见语法题 第8题

func main() {
	abc := make(chan int, 1000)
	for i := 0; i < 10; i++ {
		abc <- i
	}
	go func() {
		for {
			a := <-abc
			fmt.Println("a: ", a)
		}
	}()
	close(abc)
	fmt.Println("close")
	time.Sleep(time.Second * 100)
}

这个最大的问题是没有做channel读取的检查吧 ,loop循环的时候如果不检查是否channel可读,会一直输出类型的初始化值(0) 上面的代码会打印大量无用的0值, 只要检查后退出即可

func main() {
	abc := make(chan int, 1000)
	for i := 0; i < 10; i++ {
		abc <- i
	}
	go func() {
		for {
			a, ok  := <-abc
			if ok{
				fmt.Println("a: ", a)
			}else{
				return
			}

		}
	}()
	close(abc)
	fmt.Println("close")
	time.Sleep(time.Second * 100)
}

语法题目一 第11题,

第11题,是能正常执行完毕的,推测现在golang 的 GC 动作是不需要所有正在运行 goroutine 都停止后进行。猜测。

最接近的三数之和,代码有误

`
func threeSumClosest(nums []int, target int) int {
if len(nums) == 3 {
return nums[0] + nums[1] + nums[2]
}
sort.Ints(nums)
sum := nums[0] + nums[1] + nums[2]

  for i := 0; i < len(nums); i++ {
	  l := i+1
	  r := len(nums) - 1
	  for l < r {
		  current := nums[i] + nums[l] + nums[r]
		  if math.Abs(float64(sum - target)) > math.Abs(float64(target - current)) {
			  sum = current
		  }
		  if current < target {
			  l++
		  } else if current == target {
			  return target
		  } else {
			  r--
		  }
	  }
  }
  return sum

}

`

question/q001.md 优化

number, letter := make(chan struct{}), make(chan struct{}) letter <- struct{}{} 这种使用方式比 number, letter := make(chan bool), make(chan bool) letter <- true 更好些吧

q001.md

看到前面已经有一个关于这个问题的讨论,我还是选择了开了一个新的issue谈谈我的想法。


除了使用加锁的方法,还可以使用channel来实现goroutine调度,这是一个典型的 pingpong的问题,加上一个channel接受完成通知就好。


package main

import (
	"fmt"
)

func main() {
	ping := make(chan struct{}, 1)
	pong := make(chan struct{}, 1)
	done := make(chan struct{})

	ping <- struct{}{}
	go func() {
		for i := 0; i < 10; i++ {
			<-ping
			fmt.Print(i)
			pong <- struct{}{}
		}
	}()

	go func() {
		for i := 0; i < 10; i++ {
			<-pong
			fmt.Printf("%c", 'A'+i)
			ping <- struct{}{}
		}
		done <- struct{}{}
	}()

	<-done
	close(ping)
	close(pong)
}

语法找错题 最后一道解答错误

https://github.com/lifei6671/interview-go/blob/master/question/q014.md

下面代码写法有什么问题?

package main
import (
    "fmt"
)
type Student struct {
    Age int
}
func main() {
    kv := map[string]Student{"menglu": {Age: 21}}
    kv["menglu"].Age = 22
    s := []Student{{Age: 21}}
    s[0].Age = 22
    fmt.Println(kv, s)
}

-golang中的map 通过key获取到的实际上是两个值,第一个是获取到的值,第二个是是否存在该key。因此不能直接通过key来赋值对象-(错误解答)

是因为map中保存的是值类型 kv["menglu"] 操作是拿到map中的拷贝
如果换成 kv := map[string]*Student{"menglu": {Age: 21}} 使用指针就不会有问题

交替打印数字字母的另外一种实现方法

package main

import (
	"fmt"
	"runtime"
	"sync"
)

const N = 28

func main() {
	runtime.GOMAXPROCS(1)
	wg := &sync.WaitGroup{}
	wg.Add(N)
	for i := 0; i < N; i++ {
		go func(m int) {
			defer wg.Done()
			fmt.Printf(`%d%d`,m+1,m+2)
			runtime.Gosched()
		}(i)
		go func(m int) {
			defer wg.Done()
			if m == 26 {
				runtime.Goexit()
			}
			fmt.Printf(`%c%c`, 'A'+m, 'A'+m+1)
		}(i)
		i = i + 1
	}
	wg.Wait()
}

常见语法题目 一 的第6题,CAS操作

题目:
6、请说明下面代码书写是否正确。

var value int32

func SetValue(delta int32) {
	for {
		v := value
		if atomic.CompareAndSwapInt32(&value, v, (v+delta)) {
			break
		}
	}
}

解析:

atomic.CompareAndSwapInt32 函数不需要循环调用。

疑问:
atomic.CompareAndSwapInt32仅提供了原子交换的操作,我理解SetValue这个函数的用意应该就是让value加上delta大小的数值,所以在atomic.CompareAndSwapInt32操作失败,返回False的时候应该要循环重新读取新值然后进行CAS操作,直到atomic.CompareAndSwapInt32返回为True
解析中的不需要循环调用,是否描述不正确。

翻转字符抖机灵版

func reverseString(s string) (string, bool) {
  str := []rune(s)
  l := len(str)
  if l > 5000 {
    return "", false
  }
  for i := 0 ; i < l/2; i++ {
    str[i] = str[l-i-1] - str[i]
	str[l-i-1] = str[l-i-1] - str[i]
	str[i] = str[i] + str[l-i-1]
  }
  return string(str), true
}

类似的题目不一样的答案

题目一
题目二
位于q007和q014的这两个题目问题是一样的,但是答案不一样,q007的答案更为准确,将题目二的代码改成这样就可以正确运行:

type Student struct {
	Age int
}
func main() {
	kv := map[string]*Student{"menglu": {Age: 21}}
	kv["menglu"].Age = 22
	s := []Student{{Age: 21}}
	s[0].Age = 22
	fmt.Println(kv, s)
}

我在stackoverflow上找到了和q007类似的解答:https://stackoverflow.com/a/32751792/4737579

”判断字符串中字符是否全都不同“错别字

原文:
第二个方法使用的是golang内置方法strings.Index和strings.LastIndex,用来判断指定字符串在另外一个字符串的索引未知,分别是第一次发现位置和最后发现位置

错别字:索引位置

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.