Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
OpenDocCN
think-dast-zh
提交
5eee5e55
T
think-dast-zh
项目概览
OpenDocCN
/
think-dast-zh
8 个月 前同步成功
通知
0
Star
26
Fork
13
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
T
think-dast-zh
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
前往新版Gitcode,体验更适合开发者的 AI 搜索 >>
提交
5eee5e55
编写于
9月 24, 2017
作者:
W
wizardforcel
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
ch16
上级
f09c44b3
变更
1
隐藏空白更改
内联
并排
Showing
1 changed file
with
188 addition
and
2 deletion
+188
-2
16.md
16.md
+188
-2
未找到文件。
16.md
浏览文件 @
5eee5e55
# 第十六章 布尔搜索
> 原文:[Chapter 16 Boolean search](http://greenteapress.com/thinkdast/html/thinkdast017.html)
> 译者:[飞龙](https://github.com/wizardforcel)
> 协议:[CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/)
> 自豪地采用[谷歌翻译](https://translate.google.cn/)
在本章中,我展示了上一个练习的解决方案。然后,你将编写代码来组合多个搜索结果,并按照它与检索词的相关性进行排序。
## 16.1 爬虫的答案
...
...
@@ -125,8 +133,186 @@ public class WikiCrawler {
在这种情况下:
+
`s1`
和
`s2`
的交集是含有
的
“java”和“编程”的页面集。
+
`s1`
和
`s2`
的并集是含有
的
“java”或“编程”的页面集。
+
`s1`
和
`s2`
的交集是含有“java”和“编程”的页面集。
+
`s1`
和
`s2`
的并集是含有“java”或“编程”的页面集。
+
`s1`
与
`s2`
的差集是含有“java”而不含有“印度尼西亚”的页面集。
在下一节中,你将编写实现这些操作的方法。
## 16.4 练习 13
在本书的仓库中,你将找到此练习的源文件:
+
+
`WikiSearch.java`
,它定义了一个对象,包含搜索结果并对其执行操作。
+
`WikiSearchTest.java`
,它包含
`WikiSearch`
的测试代码。
+
`Card.java`
,它演示了如何使用
`java.util.Collections`
的
`sort`
方法。
你还将找到我们以前练习中使用过的一些辅助类。
这是
`WikiSearch`
类定义的起始:
```
java
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`
中,填充的
`and`
,
`or`
以及
`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`
方法的对象,用于比较元素。
如果你不熟悉
`Comparable`
和
`Comparator`
接口,我将在下一节中解释它们。
## 16.5 `Comparable`和`Comparator`
本书的仓库包含了
`Card.java`
,它演示了两个方式来排序
`Card`
对象的列表。这里是类定义的起始:
```
java
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`
对象拥有两个整形字段,
`rank`
和
`suit`
。
`Card`
实现了
`Comparable<Card>`
,也就是说它提供
`compareTo`
:
```
java
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`
张卡,如下所示:
```
java
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
;
}
```
并这样排序它们:
```
java
Collections
.
sort
(
cards
);
```
这个版本的
`sort`
将元素按照所谓的“自然秩序”放置,因为它由对象本身决定。
但是可以通过提供一个
`Comparator`
对象,来强制实现不同的排序。例如,
`Card`
对象的自然顺序将
`Ace`
视为最小的牌,但在某些纸牌游戏中,它的排名最高。我们可以定义一个
`Comparator`
,将
`Ace`
视为最大的牌,像这样:
```
java
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`
:
```
java
Collections
.
sort
(
cards
,
comparator
);
```
在这个顺序中,黑桃的
`Ace`
是牌组上的最大的牌;梅花二是最小的。
如果你想试验这个部分的代码,它们在
`Card.java`
中。作为一个练习,你可能打算写一个比较器,先按照
`rank`
,然后再按照
`suit`
,所以所有的
`Ace`
都应该在一起,所有的二也是。以此类推。
## 16.6 扩展
如果你完成了此练习的基本版本,你可能需要处理这些可选练习:
+
请阅读
<http://thinkdast.com/tfidf>
上的 TF-IDF,并实现它。你可能需要修改
`JavaIndex`
来计算文档频率;也就是说,每个检索词在索引的所有页面上出现的总次数。
+
对于具有多个检索词的查询,每个页面的总体相关性目前是每个检索词的相关性的总和。想想这个简单版本什么时候可能无法正常运行,并尝试一些替代方案。
+
构建用户界面,允许用户输入带有布尔运算符的查询。解析查询,生成结果,然后按相关性排序,并显示评分最高的 URL。考虑生成“片段”,它显示了检索词出现在页面的哪里。如果要为用户界面制作 Web 应用程序,请考虑将 Heroku 作为简单选项,用于 开发和部署 Java Web应用程序。见
<http://thinkdast.com/heroku>
。
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录