Stack Overflow 引入 CROKAGE,搜问题不用再东拼西凑

by admin on 2020年2月15日

许多开发者使用 Stack Overflow
搜索开发中遇到的问题,有时候需要查询的结果中有通俗的讲解,而且最好还能够带上代码
demo,但是两者同时很完美地满足要求还是比较困难的。不过最近 Stack
Overflow 官方博客上介绍了一种智能化的技术,或许可以解决这一问题。

Stack Overflow 上排名前十的与API相关的问题,stackoverflow

Stack Overflow是一个庞大的编程知识仓库,在Stack Overflow
上,数百万的提问被回答,并且这些回答都是高质量的。这就是为什么在Google搜索结果的排行榜上,Stack
Overflow 总是位居首位。

虽然Stack
Overflow上有非常多的提问,但是仍然每天都有大量的问题被提出,其中的很多都等待解答或者没有得到好的解答。因此,问题是如何找到答案的,通过Stack
Overflow是不够的。

随着成千上万的开发者使用Java的API并且在Github上分享他们的项目,这些项目可以提供很多很好的例子来展示如何使用Java的API。Java
API Example是一个提供常用Java API代码示例搜索的入口

在这篇文章中,我将会探索只通过开源的代码(jExample)能否解决投票前几名的API相关问题。“API相关的问题”指的是如何通过一些API来解决一个任务的问题。Stack
Overflow上投票靠前的问题在

对于每一个问题,最好的回答首先会被展示,随后通过Java API
examples(jExample)的解决方案也会图文并茂的展示。

第十六章 布尔搜索

原文:Chapter 16 Boolean
search

译者:飞龙

协议:CC BY-NC-SA
4.0

自豪地采用谷歌翻译

在本章中,我展示了上一个练习的解决方案。然后,你将编写代码来组合多个搜索结果,并按照它与检索词的相关性进行排序。

澳门新葡亰信誉平台游戏 1

遍历一个HashMap

被接受的回答:

    Map<String, Object> map = ...; 
    for (String key : map.keySet()) { 
     // ... 
    }

  

如果我们在jExample搜索“HashMap”,前往java.util.HashMap示例页面。然后点击其中一个最常用的方法-entrySet(),我们就能快速的如下的示例:

 HashMap<BigInteger,R> subMap = rowie.getValue();
 for( Entry<BigInteger, R> colie : subMap.entrySet() )
 {
  BigInteger col = colie.getKey();
  R vali = colie.getValue();
  ret.setVal(row, col, mutr.mutate( vali ) );
 }

  

这个例子展示了如何通过使用HashMap.entrySet(),Entry.getKey()Entry.getValue()去迭代循环去遍历一个HashMap

Links: HashMap.entrySet()

16.1 爬虫的答案

首先,我们来解决上一个练习。我提供了一个WikiCrawler的大纲;你的工作是填写crawl。作为一个提醒,这里是WikiCrawler类中的字段:

public class WikiCrawler {
    // keeps track of where we started
    private final String source;

    // the index where the results go
    private JedisIndex index;

    // queue of URLs to be indexed
    private Queue<String> queue = new LinkedList<String>();

    // fetcher used to get pages from Wikipedia
    final static WikiFetcher wf = new WikiFetcher();
}

当我们创建WikiCrawler时,我们传入source
index。最初queue只包含一个元素,source

新技术名叫 CROKAGE(Crowd Knowledge Answer
Generator,群众知识答案生成器),它来源于今年早些时候一个计算机科学研究小组发表的一篇论文,CROKAGE
可以将提问作为查询,然后返回相关的、全面的编程解决方案,所谓全面,就是说它不仅有代码片段,还包括简洁的自然语言解释,这样开发者就不用在众多的答案中拼凑出一个解决方案。

澳门新葡亰信誉平台游戏,通过一个数组创建一个ArrayList

对于这个问题,有多个回答提供了很多方式。这里是一些排名前三的方法:

    // Method 1
    new ArrayList<Element>(Arrays.asList(array))
    // Method 2
    ImmutableList.of("string", "elements");
    // Method 3
    List<String> l1 = Lists.newArrayList(anotherListOrCollection);

  

以上的三个方法可以通过`jExample1找到

Method 1:

    List<String> updatedLikedAddresses = new ArrayList<>(Arrays.asLi(likedAddresses));

  

Method 2:

    List<String> portions = ImmutableList.of(serviceName,version,callStyle.name())

  

Method 3:

    List<String> portions = ImmutableList.of(serviceName,version,callStyle.name())

  

注意,queue的实现是LinkedList,所以我们可以在末尾添加元素,并从开头删除它们

以常数时间。通过将LinkedList对象赋给Queue变量,我们将使用的方法限制在Queue接口中;具体来说,我们将使用offer添加元素,以及poll来删除它们。

这是我的WikiCrawler.crawl的实现:

    public String crawl(boolean testing) throws IOException {
        if (queue.isEmpty()) {
            return null;
        }
        String url = queue.poll();
        System.out.println("Crawling " + url);

        if (testing==false && index.isIndexed(url)) {
            System.out.println("Already indexed.");
            return null;
        }

        Elements paragraphs;
        if (testing) {
            paragraphs = wf.readWikipedia(url);
        } else {
            paragraphs = wf.fetchWikipedia(url);
        }
        index.indexPage(url, paragraphs);
        queueInternalLinks(paragraphs);
        return url;
    }

这个方法的大部分复杂性是使其易于测试。这是它的逻辑:

  • 如果队列为空,则返回null来表明它没有索引页面。
  • 否则,它将从队列中删除并存储下一个 URL。
  • 如果 URL
    已经被索引,crawl不会再次对其进行索引,除非它处于测试模式。
  • 接下来,它读取页面的内容:如果它处于测试模式,它从文件读取;否则它从
    Web 读取。
  • 它将页面索引。
  • 它解析页面并向队列添加内部链接。
  • 最后,它返回索引的页面的 URL。

我在 15.1
节展示了Index.indexPage的一个实现。所以唯一的新方法是WikiCrawler.queueInternalLinks

我用不同的参数编写了这个方法的两个版本:一个是Elements对象,包含每个段落的
DOM 树,另一个是Element对象,包含大部分段落。

第一个版本只是循环遍历段落。第二个版本是实际的逻辑。

    void queueInternalLinks(Elements paragraphs) {
        for (Element paragraph: paragraphs) {
            queueInternalLinks(paragraph);
        }
    }

    private void queueInternalLinks(Element paragraph) {
        Elements elts = paragraph.select("a[href]");
        for (Element elt: elts) {
            String relURL = elt.attr("href");

            if (relURL.startsWith("/wiki/")) {
                String absURL = elt.attr("abs:href");
                queue.offer(absURL);
            }
        }
    }

要确定链接是否为“内部”链接,我们检查 URL
是否以/wiki/开头。这可能包括我们不想索引的一些页面,如有关维基百科的元页面。它可能会排除我们想要的一些页面,例如非英语语言页面的链接。但是,这个简单的测试足以起步了。

这就是它的一切。这个练习没有很多新的材料;这主要是一个机会,把这些作品组装到一起。

CROKAGE 使用 Stack Overflow 上的数百万个 Q&A
作为训练语料库进行训练,还扩展了自然语言查询(任务描述),包括从 Stack
Overflow 中仔细挖掘出来的独特的开源软件库和功能术语。

如何在一个范围内生成碎随机数?

被接受的来自回答的解决方法:

    int randomNum = rand.nextInt((max - min) + 1) + min;

  

16.2 信息检索

这个项目的下一个阶段是实现一个搜索工具。我们需要的部分包括:

  • 一个界面,其中用户可以提供检索词并查看结果。
  • 一种查找机制,它接收每个检索词并返回包含它的页面。
  • 用于组合来自多个检索词的搜索结果的机制。
  • 对搜索结果打分和排序的算法。

用于这样的过程的通用术语是“信息检索”,你可以在
http://thinkdast.com/infret
上阅读更多信息 。

在本练习中,我们将重点介绍步骤 3 和 4 。我们已经构建了一个 2
的简单的版本。如果你有兴趣构建 Web 应用程序,则可以考虑完成步骤 1。

对于返回来的候选答案,研究人员组合了四个加权因子进行排序,他们利用传统的信息检索(IR)指标,如 TF-IDF 和非对称相关性,同时为了适应
Stack Overflow 的环境,还采用了非常适合软件特定文档的针对性排序机制。

如何将一个字符串转换成整型

最好的答案

    int foo = Integer.parseInt("1234");

  

16.3 布尔搜索

大多数搜索引擎可以执行“布尔搜索”,这意味着你可以使用布尔逻辑来组合来自多个检索词的结果。例如:

  • 搜索“java +
    编程”(加号可省略)可能只返回包含两个检索词:“java”和“编程”的页面。
  • “java OR 编程”可能会返回包含任一检索词但不一定同时出现的页面。
  • “java -印度尼西亚”可能返回包含“java”,不包含“印度尼西亚”的页面。

包含检索词和运算符的表达式称为“查询”。

当应用给搜索结果时,布尔操作符+OR-对应于集合操作
交,并和差。例如,假设

  • s1是包含“java”的页面集,
  • s2是包含“编程”的页面集,以及
  • s3是包含“印度尼西亚”的页面集。

在这种情况下:

  • s1s2的交集是含有“java”和“编程”的页面集。
  • s1s2的并集是含有“java”或“编程”的页面集。
  • s1s2的差集是含有“java”而不含有“印度尼西亚”的页面集。

在下一节中,你将编写实现这些操作的方法。

简单来讲,整个运作机制如前边的图片所示,主要分为几个步骤:

如何将字节流转换成字节数组

被采纳的回答

    InputStream is; 
    byte[] bytes = IOUtils.toByteArray(is);

  

16.4 练习 13

在本书的仓库中,你将找到此练习的源文件:

    • WikiSearch.java,它定义了一个对象,包含搜索结果并对其执行操作。
  • WikiSearchTest.java,它包含WikiSearch的测试代码。
  • Card.java,它演示了如何使用java.util.Collectionssort方法。

你还将找到我们以前练习中使用过的一些辅助类。

这是WikiSearch类定义的起始:

public class WikiSearch {

    // map from URLs that contain the term(s) to relevance score
    private Map<String, Integer> map;

    public WikiSearch(Map<String, Integer> map) {
        this.map = map;
    }

    public Integer getRelevance(String url) {
        Integer relevance = map.get(url);
        return relevance==null ? 0: relevance;
    }
}

WikiSearch对象包含 URL
到它们的相关性分数的映射。在信息检索的上下文中,“相关性分数”用于表示页面多么满足从查询推断出的用户需求。相关性分数的构建有很多种方法,但大部分都基于“检索词频率”,它是搜索词在页面上的显示次数。一种常见的相关性分数称为
TF-IDF,代表“检索词频率 – 逆向文档频率”。你可以在
http://thinkdast.com/tfidf
上阅读更多信息 。

你可以选择稍后实现 TF-IDF,但是我们将从一些更简单的 TF 开始:

  • 如果查询包含单个检索词,页面的相关性就是其词频;也就是说该词在页面上出现的次数。
  • 对于具有多个检索词的查询,页面的相关性是检索词频率的总和;也就是说,任何检索词出现的总次数。

现在你准备开始练习了。运行ant build来编译源文件,然后运行
ant WikiSearchTest。像往常一样,它应该失败,因为你有工作要做。

WikiSearch.java中,填充的andor以及minus的主体,使相关测试通过。你不必担心testSort

你可以运行WikiSearchTest而不使用Jedis,因为它不依赖于 Redis
数据库中的索引。但是,如果要对索引运行查询,则必须向文件提供有关Redis服务器的信息。详见
14.3 节。

运行ant JedisMaker来确保它配置为连接到你的 Redis
服务器。然后运行WikiSearch,它打印来自三个查询的结果:

  • “java”
  • “programming”
  • “java AND programming”

最初的结果不按照特定的顺序,因为WikiSearch.sort是不完整的。

填充sort的主体,使结果以递增的相关顺序返回。我建议你使用java.util.Collections提供的sort方法,它可以排序任何种类的List。你可以阅读
http://thinkdast.com/collections
上的文档 。

有两个sort版本:

  • 单参数版本接受列表并使用它的compareTo方法对元素进行排序,因此元素必须是Comparable
  • 双参数版本接受任何对象类型的列表和一个Comparator,它是一个提供compare方法的对象,用于比较元素。

如果你不熟悉ComparableComparator接口,我将在下一节中解释它们。

  • A、语料库准备
  • B、构建模型,map、索引
  • C、搜索相关答案
  • D、整合编程解决方案

如何生成一个MD5散列

可以使用MessageDigest

public static String getMD5Digest(String str) {
 try {
  byte[] buffer = str.getBytes();
  byte[] result = null;
  StringBuffer buf = null;
  MessageDigest md5 = MessageDigest.getInstance("MD5");
  // allocate room for the hash
  result = new byte[md5.getDigestLength()];
  // calculate hash
  md5.reset();
  md5.update(buffer);
  result = md5.digest();
  // System.out.println(result);
  // create hex string from the 16-byte hash
  buf = new StringBuffer(result.length * 2);
  for (int i = 0; i < result.length; i++) {
   int intVal = result[i] & 0xff;
   if (intVal < 0x10) {
    buf.append("0");
   }
   buf.append(Integer.toHexString(intVal).toUpperCase());
  }
  return buf.toString();
 } catch (NoSuchAlgorithmException e) {
  System.err.println("Exception caught: " + e);
  e.printStackTrace();
 }
 return null;
    }

  

16.5 ComparableComparator

本书的仓库包含了Card.java,它演示了两个方式来排序Card对象的列表。这里是类定义的起始:

public class Card implements Comparable<Card> {

    private final int rank;
    private final int suit;

    public Card(int rank, int suit) {
        this.rank = rank;
        this.suit = suit;
    }

Card对象拥有两个整形字段,ranksuitCard实现了Comparable<Card>,也就是说它提供compareTo

    public int compareTo(Card that) {
        if (this.suit < that.suit) {
            return -1;
        }
        if (this.suit > that.suit) {
            return 1;
        }
        if (this.rank < that.rank) {
            return -1;
        }
        if (this.rank > that.rank) {
            return 1;
        }
        return 0;
    }

compareTo规范表明,如果this小于that,则应该返回一个负数,如果它更大,则为正数,如果它们相等则为0

如果使用单参数版本的Collections.sort,它将使用元素提供的compareTo方法对它们进行排序。为了演示,我们可以列出52张卡,如下所示:

    public static List<Card> makeDeck() {
        List<Card> cards = new ArrayList<Card>();
        for (int suit = 0; suit <= 3; suit++) {
            for (int rank = 1; rank <= 13; rank++) {
                Card card = new Card(rank, suit);
                cards.add(card);
            }
        }
        return cards;
    }

并这样排序它们:

        Collections.sort(cards);

这个版本的sort将元素按照所谓的“自然秩序”放置,因为它由对象本身决定。

但是可以通过提供一个Comparator对象,来强制实现不同的排序。例如,Card对象的自然顺序将Ace视为最小的牌,但在某些纸牌游戏中,它的排名最高。我们可以定义一个Comparator,将Ace视为最大的牌,像这样:

        Comparator<Card> comparator = new Comparator<Card>() {
            @Override
            public int compare(Card card1, Card card2) {
                if (card1.getSuit() < card2.getSuit()) {
                    return -1;
                }
                if (card1.getSuit() > card2.getSuit()) {
                    return 1;
                }
                int rank1 = getRankAceHigh(card1);
                int rank2 = getRankAceHigh(card2);

                if (rank1 < rank2) {
                    return -1;
                }
                if (rank1 > rank2) {
                    return 1;
                }
                return 0;
            }

            private int getRankAceHigh(Card card) {
                int rank = card.getRank();
                if (rank == 1) {
                    return 14;
                } else {
                    return rank;
                }
            }
        };

该代码定义了一个匿名类,按需实现compare。然后它创建一个新定义的匿名类的实例。如果你不熟悉
Java 中的匿名类,可以在
http://thinkdast.com/anonclass
上阅读它们。

使用这个Comparator,我们可以这样调用sort

        Collections.sort(cards, comparator);

在这个顺序中,黑桃的Ace是牌组上的最大的牌;梅花二是最小的。

如果你想试验这个部分的代码,它们在Card.java中。作为一个练习,你可能打算写一个比较器,先按照rank,然后再按照suit,所以所有的Ace都应该在一起,所有的二也是。以此类推。

研究团队还对结果进行了对比,如下图所示,在对比中,CROKAGE
给出的建议答案中,代码示例的相关性、代码解释的清晰度以及整体解决方案的质量(也就是代码+解释的整体质量),比目前最先进的工具产出了更好的结果。

在java中如何创建一个文件并向文件中写入内容

方法一

    PrintWriter writer = new PrintWriter("the-file-name.txt", "UTF-8");       
    writer.println("The first line"); 
    writer.println("The second line"); 
    writer.close();

  

方法 2

    List<String> lines = Arrays.asList("The first line", "The second line"); 
    Path file = Paths.get("the-file-name.txt"); 
    Files.write(file, lines, Charset.forName("UTF-8"));

  

在java中从文本文件读取内容的最好方法

    BufferedReader br = new BufferedReader(new FileReader("file.txt")); 
    try {
       StringBuilder sb = new StringBuilder();
       String line = br.readLine();
       while (line != null) {
      sb.append(line);
      sb.append(System.lineSeparator());
      line = br.readLine(); 
       } 
       String everything = sb.toString(); 
    } finally { 
       br.close(); 
    }

  

16.6 扩展

如果你完成了此练习的基本版本,你可能需要处理这些可选练习:

  • 请阅读
    http://thinkdast.com/tfidf
    上的
    TF-IDF,并实现它。你可能需要修改JavaIndex来计算文档频率;也就是说,每个检索词在索引的所有页面上出现的总次数。
  • 对于具有多个检索词的查询,每个页面的总体相关性目前是每个检索词的相关性的总和。想想这个简单版本什么时候可能无法正常运行,并尝试一些替代方案。
  • 构建用户界面,允许用户输入带有布尔运算符的查询。解析查询,生成结果,然后按相关性排序,并显示评分最高的
    URL。考虑生成“片段”,它显示了检索词出现在页面的哪里。如果要为用户界面制作
    Web 应用程序,请考虑将 Heroku 作为简单选项,用于 开发和部署 Java
    Web应用程序。见
    http://thinkdast.com/heroku。

澳门新葡亰信誉平台游戏 2

如何将java.util.Date转换成XMLGregorianCalendar

被接受的回答:

    GregorianCalendar c = new GregorianCalendar(); 
    c.setTime(yourDate); 
    XMLGregorianCalendar date2 = DatatypeFactory.newInstance().newXMLGregorianCalendar(c);

  

目前 CROKAGE 还有一些局限性,比如它仅支持 Java
语言的相关查询,而且如果问题描述比较差,那工具也无法给出如何改进的建议。

如何检查一个字符串是否为数值型的字符串

被接受的回答是使用Apache Commons Lang包中的 StringUtils.isNumeric

    StringUtils.isNumeric("23432")

  

Overflow
上排名前十的与API相关的问题,stackoverflow Stack
Overflow是一个庞大的编程知识仓库,在Stack Overflow
上,数百万的提问被回答,并且…

开发者可以通过 http://www.isel.ufu.br:9000 体验该功能。

澳门新葡亰信誉平台游戏 3

关于该功能的更多信息,查看:

  • https://stackoverflow.blog/2019/08/14/crokage-a-new-way-to-search-stack-overflow

(文/开源中国)    

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图