我还在学习scala,所以这可能是一个简单回答的问题,但我一直坚持写一个方法一遍又一遍,无法编译这段代码
我正在玩Play Framework和一个反应性mongo模板,以了解Scala和Play如何工作。 我有一个带有一些方法的控制器,一个用于REST服务的端点。
问题在于以下方法,该方法接受json对象列表并使用mongo被动驱动程序更新这些对象。该类有一个成员dir1 = './INPUT/rectifiedimages/im1_rec.jpg'
dir2 = './INPUT/rectifiedimages/im2_rec.jpg'
image1 = cv2.imread(dir1, 0) # queryimage # left image
image2 = cv2.imread(dir2, 0) # trainimage # right image
# Initiate SIFT detector
orb = cv2.ORB_create()
# find the keypoints and descriptors with SIFT
kp1, des1 = orb.detectAndCompute(image1,None)
kp2, des2 = orb.detectAndCompute(image2,None)
# BFMatcher with default params
bf = cv2.BFMatcher()
matches = bf.knnMatch(des1, des2, k=2)
# Apply ratio test
good = []
for m,n in matches:
if m.distance <= 1.*n.distance:
good.append(m)
src_pts = np.float32([kp1[m.queryIdx].pt for m in good]).reshape(-1, 1, 2)
dst_pts = np.float32([kp2[m.trainIdx].pt for m in good]).reshape(-1, 1, 2)
F, maskF = cv2.findFundamentalMat(src_pts, dst_pts, cv2.RANSAC, 1., 1.) #
cv2.CV_FM_LMEDS cv2.RANSAC
#--- remove masked points outliers
good = [good[i] for i,v in enumerate(maskF) if v[0] == 1]
src_pts = np.float32([kp1[m.queryIdx].pt for m in good]).reshape(-1, 1, 2)
dst_pts = np.float32([kp2[m.trainIdx].pt for m in good]).reshape(-1, 1, 2)
H, maskH = cv2.findHomography(src_pts, dst_pts, cv2.FM_LMEDS, 0., 0.99)
mask = maskH
matchesMask = mask.ravel().tolist()[![enter image description here][1]][1]
# cv2.drawMatchesKnn expects list of lists as matches.
draw_params = dict(matchColor = (0,255,0), # draw matches in green color
singlePointColor = (0,0,255),
matchesMask = matchesMask, # draw only inliers
flags = 0)
img3 = cv2.drawMatches(image1, kp1, image2, kp2,good, None,**draw_params)
,其类型为citiesFuture
。
我可以在此处找到我添加此方法的原始类代码:CityController on github
Future[JSONCollection]
我已经设法通过大量的试验和错误来实现这一目标,但是我开始理解scala和Futures的一些机制是如何工作的,我认为:)我认为我&#39; m close,但我的IDE仍然只在行def updateAll() = Action.async(parse.json) { request =>
Json.fromJson[List[City]](request.body) match {
case JsSuccess(givenCities, _) =>
citiesFuture onComplete[Future[Result]] { cities =>
val updateFutures: List[Future[UpdateWriteResult]] = for {
city <- givenCities
} yield cities.get.update(City.getUniqueQuery(city), Json.obj("$set" -> city))
val promise: Promise[Result] = Promise[Result] {
Future.sequence(updateFutures) onComplete[Result] {
case s@Success(_) =>
var count = 0
for {
updateWriteResult <- s.value
} yield count += updateWriteResult.n
promise success Ok(s"Updated $count cities")
case Failure(_) =>
promise success InternalServerError("Error updating cities")
}
}
promise.future
}
case JsError(errors) =>
Future.successful(BadRequest("Could not build a city from the json provided. " + Errors.show(errors)))
}
}
上方的单个结束大括号上给出了一个检查错误。
错误如下:单位类型的表达式不符合预期类型Nothing 。 我已经检查了Promise和onComplete代码块的预期返回值,但我不相信他们希望 Nothing 作为返回类型。
有人可以向我解释一下我错过了什么,而且,我确信这可以做得更好,所以如果你有任何我可以学习的提示,请告诉我!
答案 0 :(得分:1)
你有点走在正确的轨道上,但正如@cchantep所说,一旦你在Future
- 土地上运作,你需要创建自己的{{1}是非常不寻常的}}
此外,看到Promise.future
被使用实际上是非常不寻常的 - 惯用的Scala通常偏向于更高级别的&#34;对期货的onComplete
ping的抽象。我将尝试演示如何在Play控制器中编写您的功能:
首先,&#34;端点&#34;只需处理一件事 - 与外界联系 - 即JSON解析部分。如果一切都转换为OK,它会调用一个实际完成工作的私有方法(map
):
performUpdateAll
下一步,我们有私有功能,可以执行多个城市的更新。再次,尝试遵守Single Responsibility Principle(在功能意义上 - 一个功能应该做一件事),我已经提取出def updateAll() = Action.async(parse.json) { request =>
Json.fromJson[List[City]](request.body) match {
case JsSuccess(givenCities, _) =>
performUpdateAll(givenCities)
case JsError(errors) =>
Future.successful(BadRequest("Could not build a city from the json provided. "))
}
}
知道如何更新一个城市< / em>并返回updateCity
。一个很好的副作用是代码重用;您可能会发现您可以在其他地方使用此类功能。
Future[UpdateWriteResult]
据我所知,这将与您打算工作的方式完全相同。但是,通过使用private def performUpdateAll(givenCities:List[City]):Future[Result] = {
val updateFutures = givenCities.map { city =>
updateCity(city)
}
Future.sequence(updateFutures).map { listOfResults =>
if (listOfResults.forall(_.ok)) {
val count = listOfResults.map(_.n).sum
Ok(s"Updated $count cities")
} else {
InternalServerError("Error updating cities")
}
}
}
代替其较低级别的对应Future.map
并匹配Future.onComplete
和Success
,您可以获得更简洁的代码(在我看来)&#39;更容易看到意图因为它周围的样板很少。
我们仍然会检查每次更新是否都有效:
Failure
我认为读得很好 - 所有结果都必须好!
我做的另一个小技巧就是更换你的&#34;计数&#34;使用可变变量的逻辑,带有单行:
if (listOfResults.forall(_.ok))
变为:
var count = 0
for {
updateWriteResult <- s.value
} yield count += updateWriteResult.n
即。将结果列表转换为整数列表(val count = listOfResults.map(_.n).sum
中的n
),然后使用列表中提供的内置UpdateWriteResult
函数来完成剩下的工作。