使用 AI 为开发提速

与 AI 同行。

出题

最直接使用 AI 的方法,就是出题。出题方式适用于写工具类。

如 “再谈函数式编程:释放编程创造力” 一文所示。


再给一例:

AI + 函数式 + 泛型编程,将能让你的编程效率成倍提升。

package util

import (
    "runtime"
    "sync"
)

/*
 * 接收一个普通函数 plainFunc,返回一个可以并发调用 plainFunc 的函数,这个函数接收一个列表,返回另一个列表
 * 这个结果列表由单个函数调用结果汇集而成
 */
func ConcurrentExecute[T any, R any](plainFunc func([]T) []R) func([]T) []R {
    return func(list []T) []R {
        ch := make(chan []R)
        var wg sync.WaitGroup

        subListSize := len(list) / runtime.NumCPU()
        if subListSize == 0 {
            subListSize = 1
        }

        for i := 0; i < len(list); i += subListSize {
            end := i + subListSize
            if end > len(list) {
                end = len(list)
            }
            wg.Add(1)

            go func(start, end int) {
                defer wg.Done()
                ch <- plainFunc(list[start:end])
            }(i, end)
        }

        go func() {
            wg.Wait()
            close(ch)
        }()

        var finalResult []R
        for resultPart := range ch {
            finalResult = append(finalResult, resultPart...)
        }

        return finalResult
    }
}

利用 AI 生成自动化工具

更复杂的出题,让 AI 生成自动化的完整实用工具。注意要详述要求。

比如,java 工程转 Go,需要把大量 Java 对象转成 Go 结构体。需要一个批量转换工具,不是手工编码。

写一个 java 程序,对于给定 java 对象,将其转成 Go 结构体。

比如


public class ScriptDTO {

    @JsonProperty("pguid")
    private String pguid;

    /**
     * 脚本类型
     */
    @JsonProperty("type")
    private String type;

   @JsonProperty("rule")
    private Rule rule;

    @JsonProperty("sha256s")
    private List<String> sha256s;

     @JsonProperty("param")
    private Map<String, Object> param;

    @JsonProperty("create_time")
    private Long createTime;

    
   
}

转成

type AgentScript struct {

    Pguid string `json:"pguid"`
    Type string `json:"type"`
    Rule Rule `json:"rule"`
    CreateTime int64 `json:"create_time"`
    Sha256s []string `json:"sha256s"`
    Param map[string]any `json:"param"`

}

要求满足如下条件:

  1. 类名、字段名、字段类型可以反射方式获取
  2. Java 字段类型要转换为 go 的对应字段类型
  3. go 结构体字段与 Java 类定义的字段顺序保持一致。
  4. Java 注释内容 /** comment */ 写在 结构体字段定义的后面,以 // 开始;如果没有则省略。
  5. 考虑字段没有 @JsonProperty("rule") 注解的空情形。
  6. 要求能够处理 java 里的各种原子类型,比如 int, Integer, String, Boolean, Long, long; 自定义类型按原样输出。
  7. go 结构体字段要首字母大写。
  8. 将 go 结构体写入用户主目录下 ~/ 的文件。文件名是类名的小写下划线形式,去掉最前面的下划线。
  9. 保持程序的模块化和易修改,做好错误处理。
  10. 请给出完整可用程序,不要伪代码。

写一个 java 完整程序,指定一个 java 包名,对这个包及其子包下的所有 后缀为 DTO.java ,或 DO.java 的 java 类,根据上述方法生成对应 go 结构体,并写入对应文件,写入用户主目录下 ~/ 的文件。文件名是类名的小写下划线形式,去掉最前面的下划线。 结构层级与包层级保持一致。

有时,AI 不能一次性生成完整程序,则需要出多道题把答案拼起来。

固定模式代码

比如设计模式代码。指定设计模式名称,则可直接生成对应代码。

例如:“Go 模板:用代码生成代码”一文中用 AI 生成生成器模式代码。

类似还有工厂、策略模式代码等。

提供样例

如果要生成的代码比较多但有固定样式,则可以提供样例。如下所示:

请记住如下样例:

给定一个 java 枚举:

public enum DetectionMethodEnum {

    PROCESS_HASH("process_hash", "进程Hash检测"),

    private final String type;
    private final String desc;

}

生成对应的 go 枚举为:

type DetectionMethod string

type DetectionMethodInfo struct {
    MethodType string
    Desc       string
}

const (
    ProcessHash  DetectionMethod = "PROCESS_HASH"
)

var DetectionMethodMap = map[DetectionMethod]DetectionMethodInfo{
    ProcessHash: {
        MethodType: "process_hash",
        Desc:       "进程Hash检测"
    }
}

请严格按照样例,为下面的 java 枚举生成 go 枚举。

@Getter
@AllArgsConstructor
public enum ContainerTypeEnum {

    DOCKER("docker", "docker_container"),
    CONTAINERD("cri", "cri_containerd"),
    CRIO("crio", "crio_container");

    private final String type;
    private final String name;

}


提供模板

与提供样例类似。模板通过若干变量生成。

请记住如下样例:

给定 do 类 DetectionDO, 生成:

type DetectionService interface {
	service.BaseService[*models.DetectionDO]
}

type DetectionServiceImpl struct {
	service.BaseService[*models.DetectionDO]
}

func NewDetectionService(repo repository.DetectionRepository) DetectionService {
	return &DetectionServiceImpl{
		BaseService: service.NewMongoService[*models.DetectionDO](repo),
	}
}

func init() {
	dow.ProvideGlobal[DetectionService](nil, func(injector *do.Injector) (DetectionService, error) {
		repo, err := dow.InvokeGlobal[repository.DetectionRepository](nil)
		if err != nil {
			return nil, err
		}
		return NewDetectionService(repo), nil
	})
}
type DetectionRepository interface {
	repository.BaseRepository[*models.DetectionDO]
}

type DetectionRepositoryImpl struct {
	repository.BaseRepository[*models.DetectionDO]
}

func NewDetectionRepository(db *mongox.Database) DetectionRepository {
	return &DetectionRepositoryImpl{
		BaseRepository: repository.NewMongoRepository[*models.DetectionDO](db.Collection(models.DetectionDO{}.TableName())),
	}
}

func init() {
	dow.ProvideGlobal[DetectionRepository](nil, func(injector *do.Injector) (DetectionRepository, error) {
		database, err := mongox.TenantDatabase()
		if err != nil {
			return nil, err
		}
		return NewDetectionRepository(database), nil
	})
}

请为 DO 类 IdsFileUploadTaskDO 生成 service 和 repository 代码。

语言翻译

将如下 Java 代码转换为 Go 代码:

public static String[] getCmdOutput(String cmd) throws InterruptedException, IOException {
        long start = System.currentTimeMillis();
        Process process = new ProcessBuilder().command("sh", "-c", cmd).start();
        boolean ret = process.waitFor(3, TimeUnit.SECONDS);
        String output = StringUtils.EMPTY;
        String error = IOUtils.toString(process.getErrorStream(), StandardCharsets.UTF_8);
        if (ret) {
            output = IOUtils.toString(process.getInputStream(), StandardCharsets.UTF_8);
            LOG.info("cmd: {} output: {}", cmd, output);
        } else {
            LOG.warn("cmd:{} ret is false", cmd);
            process.destroyForcibly();
        }
        long end = System.currentTimeMillis();
        LOG.info("exec cmd: {} cost: {} ms", cmd, (end-start));
        return new String[] { output, error };
    }

分离纯函数

很多业务方法,大部分都是纯逻辑(输入相同则输出也相同,具有引用透明性),只有少量是依赖外部服务的。

这时候,可以运用“分离纯函数”的技巧,将方法中的纯逻辑与依赖分离。AI 特别适合于生成无语境或无依赖的纯函数。

可参考 “改善代码可测性的若干技巧”和“使用Java函数接口及lambda表达式隔离和模拟外部依赖更容易滴单测”这两篇文章。

具体方法是: 先对方法进行拆解,把外部依赖部分抽取出来。外部依赖部分可以用函数参数替代,这样这个方法就变成了一个纯函数,可以用 AI 自动生成,并用 AI 生成测试用例和单测。

仔细再审视我们的工程代码,会发现,其实 80% 都是纯逻辑,真正的业务部分可能只占 20%。经过良好抽象之后,把纯逻辑提取出来,把业务部分做成配置,则理论上 80% 的代码可以用 AI 生成。

AI 代码纠错

定义

type ValueModifier[T any] func(T, ...any)

func DuplicateAndModifyElement[T any](list []*T, keyFunc funcs.IdKey[T], mFunc funcs.ValueModifier[*T], args ...any) []*T {
    seen := make(map[string]bool)
    result := make([]*T, 0, len(list))

    for _, elem := range list {
        key := keyFunc(*elem)
        if !seen[key] {
            seen[key] = true
            mFunc(elem, args)
            result = append(result, elem)
        }
    }

    return result
}

为什么 这行报错,如何修复

pb := []*test.PersonBasicInfo{{Name: "qin", Age: 36}, {Name: "ni", Age: 28}, {Name: "qin", Age: 29}, {Name: "ni", Age: 28}}
 
undupAndModified := util.DuplicateAndModifyElement(pb, func(info test.PersonBasicInfo) string { return info.Name }, func(p *test.PersonBasicInfo, skills ...string) { p.Skills = skills }, "programming")

AI CodeReview

现在 gitlab 已经集成了 AI Code review 。这些 review 还是相对中肯的。


小结

对于高级程序员来说, AI 是一个强大的工具,真如虎添翼。

如何利用 AI 来提升效率和创造性,是一个很好的话题。

更多姿势还在解锁中。。。

posted @ 2024-02-24 12:08  琴水玉  阅读(50)  评论(0编辑  收藏  举报