如何使用 Go 在 Java 中进行编程?

昨天我不得不在go中执行一个任务。

正如您可能已经从本文中了解到的那样,我最近开始使用Go进行编程,因此,我脑海中常浮现的第一个问题是:我将如何在Java中进行编程的?

我不确定它是好是坏,但我发现从多个角度看同一件事对我有些好处。这不是使用Go或Java,而是使用函数式或命令式,并混合不同的概念,我从中看到了真正的价值。

我将记录如何从不同的角度看待问题,从而形成解决方案。

问题

我将仅简单地完成一些任务,来突出本文中的有趣之处。 任务的一部分涉及将文件的一部分加载到内存中:一个超级简单的解析器。

该文件如下所示:

// a comment here 
#HEADER 
Key1=value1 
//another comment 
Key2=value2 
Key3=value3 
Key4=value4 

我们对属于一个部分的两个键感兴趣。 该部分以标头开头,标头以井号标签开头。 我们将获得该部分的名称和两个键的名称。 即部分:“人”,键:[“名称”,“姓”] 因此,我们想在文件中找到“人”部分,并将名称和姓氏的值加载到内存中。

关于文件的一些注意事项:

  • 标头可能不在文件中
  • 文件中可能没有名称和/或
  • 姓氏名称始终在文件中位于姓氏之前
  • 它可能包含注释(//)或其他标记

一种可能的解决方案是简单地逐行读取文件并:

  • 如果该行以#开头,则这是节的开始,请准备读取此标头的密钥对
  • 读名字和姓氏
  • 忽略注释和其余键

所以,让我们暂时戴上Java程序员的帽子。

所以,逐行读取文件,过滤某些行……

这看起来像是java.nio.file的工作、java.util.stream文件.流和自定义java.util.函数、谓词过滤流。
我说习惯,意思是我们需要在谓词中保持某种状态。状态只是谓词下次调用时应该匹配的字符串,也就是说,如果它已经匹配了报头,那么应该匹配name next。

这基本上意味着两件事:Stream和Lambda函数。

简而言之,Golang中没有像流这样的东西。 让我在这里稍作改动,这种精神锻炼的全部目的不是实现与语言本身相反的东西,而是从中获得最大的收益。 所以没有流的话, 我们将在没有它的情况下生活来进行救援。

但是lambdas呢? 好吧,Go中的功能是一等公民。 我们可以将它们分配给变量、创建函数数组,像其他任何参数一样传递它们,所以我想我们有lambda :)


好的讨论,让我们看一下代码... 在查看代码之前,请先回顾一下我们想做的事情。 因此,我们希望有一个for循环,在该循环中,我们逐行从源代码中读取内容,并针对每一行检查是否应将其写入某处。 检查是否应写入该行的位是一个过滤器,该过滤器在内部保持状态。

好吧,让我们看看这一点:

func copy(profileName string, in io.Reader, out io.Writer) error {
    var (
        line       string
        readError  error
        writeError error
    )

    profileFilter := newProfileFilter(profileName)
    reader := bufio.NewReader(in)

    for {
        line, readError = reader.ReadString('\n')

        if profileFilter.match(line) {
            _, writeError = io.WriteString(out, line)
        }

        if readError != nil || writeError != nil {
            break
        }
    }

     if writeError != nil {
        return writeError
     }

     if readError != io.EOF {
        return readError
     }

     return nil
}

这与Java几乎“等效”


Files.lines(resource())
             .filter(profileFilter)
             .forEach(ProfileWriter::write); 

让我们分解一下:

  • Files.lines是for循环
  • 过滤器是if条件profileFilter.match(行)
  • forEach只是if条件io的主体。WriteString(线),

因此,让我们看一下过滤器:

func newProfileFilter(profileName string) *profileFilter {
    var matchers [](func(line string) bool)

    matchers = append(matchers,
        matcher(startsWith).apply("#"+profileName),
        matcher(startsWith).apply("name="),
        matcher(startsWith).apply("surname="),
    )

    return &profileFilter{matchers, profileName}
}

func startsWith(line string, toMatch string) bool {
    return strings.HasPrefix(
        strings.ToLower(strings.TrimSpace(line)),
        strings.ToLower(toMatch),
    )
}

这是profileFilter的构造函数,它负责检测该节的标题和键。 如果需要的话,它基本上是一个函数或lambda数组。 每次过滤器与文件中的一行匹配时,我们都会从数组中弹出一个元素,因此在下一次迭代中,过滤器将与其他字符串匹配。 我从代码中真正喜欢的是我们构造lambda的方式:

matcher(startsWith).apply("name=")


因此,我们正在构建一个匹配器,它匹配以“name=”开始的行
什么是真正的匹配器?
type matcher func(line string, toMatch string) bool

这是函数的类型别名。 因此,这只是一个函数,它获得输入作为匹配的行和字符串并返回布尔值。就是这样。 语法Matcher(startsWith)只是一个强制转换,我们正在将功能startsWith强制转换为匹配器。 当然可以做到这一点,因为匹配器是一个函数!

当我写这篇文章的时候,我就像...好吧,go中的函数真是太酷了

那个应用方法是什么?

func (f matcher) apply(toMatch string) func(line string) bool {
    return func(line string) bool {
        return f(line, toMatch)
    }
}

这只是一种处理函数的方法。长话短说,我们有一个有两个参数的函数,我们想要一个有一个参数的函数另一个参数已经设置好了。

在这里,我们应用我们想要匹配的字符串,比如"name="然后我们得到一个函数它的参数只有一行。
我们将apply方法附加到matcher中,所以最终的结果是通过调用:

matcher(startsWith).apply("name=") 

我们返回一个函数,该函数返回一个布尔值(java域中的Predicate),如果该行以name开头,则该布尔值为true。

仅出于完整性考虑,这是这里的其余代码。

type profileFilter struct {
    matchers    []func(line string) bool
    profileName string
}

func (p *profileFilter) match(text string) bool {
    if len(p.matchers) == 0 {
        return false
    }

    shouldFilter := p.matchers[0](text)

    if shouldFilter {
        p.matchers = p.matchers[1:len(p.matchers)]
    }

    return shouldFilter
}

Java 版本

在写这篇文章时,我是为了娱乐,实际上是可以用Java编写相同的程序。 这是一个快速实现:


    public static void main(String[] args) throws IOException, URISyntaxException{
        Matcher startsWith = (line, toMatch) -> startsWith(line, toMatch);

        ProfileFilter profileFilter =
            new ProfileFilter(
                startsWith.apply("#some-profile"),
                startsWith.apply("key2="),
                startsWith.apply("key3=")
            );

        Files.lines(resource())
             .filter(profileFilter)
             .forEach(System.out::println);
    }

    static class ProfileFilter implements Predicate<String> {
        private LinkedList<Predicate<String>> predicates;

        ProfileFilter(Predicate<String>...predicates) {
            this.predicates = new LinkedList<>(Arrays.asList(predicates));
        }

        @Override
        public boolean test(String s) {
            if (predicates.size() == 0) {
                return false;
            }

            boolean shouldFilter = predicates.getFirst().test(s);

            if (shouldFilter) {
                predicates.removeFirst();
            }

            return shouldFilter;
        }
    }

    interface Matcher extends BiPredicate<String, String> {

        default Predicate<String> apply(String applied) {
            return s -> this.test(s, applied);
        }
    }

    static Boolean startsWith(String line, String toMatch) {
        return line.toLowerCase().trim().startsWith(toMatch.toLowerCase());
    }

这两个版本显然非常相似,不同的是,在Java版本中,我使用流而不是for循环,并且在控制台上打印行而不是将其写入Writer。

这里有一个附加说明:流确实很酷,但是它们不能处理异常。例如,写入文件就是引发异常的操作之一,因此在实际的用例中,我认为我要么使用for循环,而不是流,要么使用RxJava Observable。

结论

好吧,这比我预期的要长,考虑到我只想说Golang可能不是那么糟糕的XD。
玩笑归玩笑,希望你喜欢。

谢谢!

资源资源

文章代码:github
我找到了两个关于Golang函数的有趣的演讲。
完全值得一看:
闭包是go的泛型
不要成就一流的功能

 

原文链接: https://dev.to//napicella/go-for-java-developers-lambda-functions-to-the-rescue-655

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值