In RxAndroidBle, How do return ObservableSource<out TypeVariable<R>!>! from Observable.combineLatest() instead of Unit? - rx-java2

I am trying to get all characteristics from the Device Information service on a specific device. I can pick any single device and get the characteristic from it using:
deviceConnection =
device.establishConnection(true)
.flatMapSingle { rxBleConnection ->
rxBleConnection
.readCharacteristic(java.util.UUID.fromString(c.uuid))
}
.subscribe(
{ onConnectionSuccess(it, v) },
{ onConnectionFailure(it, v) })
The issue is when I attempt to read multiple characteristics at once. I have tried to use the example documentation from Polidea's blog detailing multiple reads, multiple questions on SO, and documentation for RxJava observables and I cannot figure out what I'm doing wrong.
The error I am getting from AndroidStudio is:
Type mismatch.
Required: ObservableSource<out TypeVariable<R>!>!
Found: Unit
As far as I can tell I'm doing this the same way all of these other examples are. The code I am attempting to read multiple characteristics is below:
device = BleApplication.getRxBleClient(this)
.getBleDevice(macAddress)
deviceConnection =
device.establishConnection(false)
.flatMap { rxBleConnection ->
Observable.combineLatest(
rxBleConnection
.readCharacteristic(jUuid(ManufacturerNameString.toString())),
rxBleConnection
.readCharacteristic(jUuid(ModelNumberString.toString())),
rxBleConnection
.readCharacteristic(jUuid(SerialNumberString.toString())),
rxBleConnection
.readCharacteristic(jUuid(HardwareRevisionString.toString())),
rxBleConnection
.readCharacteristic(jUuid(FirmwareRevisionString.toString())),
rxBleConnection
.readCharacteristic(jUuid(SoftwareRevisionString.toString())),
rxBleConnection
.readCharacteristic(jUuid(SystemID.toString())),
rxBleConnection
.readCharacteristic(jUuid(PnPID.toString())),
::BoseDevice
)
}
.take(1)
.subscribe({}, {}) //onConnectionSuccess(it) }, { onConnectionFailure(it) })
The class I've trying to feed the observables into:
internal class BoseDevice(
val manufacturer: ByteArray,
val modelNumber: ByteArray,
val serialNumber: ByteArray,
val hardwareRevision: ByteArray,
val firmwareRevision: ByteArray,
val softwareRevision: ByteArray,
val systemID: ByteArray,
val pnpID: ByteArray
)
These are the my dependencies declared in my gradle file:
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.core:core-ktx:1.3.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation "com.polidea.rxandroidble2:rxandroidble:1.11.1"
implementation 'com.google.android.material:material:1.1.0'
implementation "io.reactivex.rxjava2:rxjava:2.2.19"
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'

These type problems are notoriously hard to debug with Kotlin (at least now with 1.3.70).
You have two mistakes that prevent you from having working code:
First — you try to .combineLatest() objects of type Single, not Observable. So you should use either .flatMapSingle() combined with Single.zip() or change each read to an Observable like this rxBleConnection.readCharacteristic().toObservable().
Second — you try to use constructor as a Function8 object which apparently cannot be used this way. You have to create this function by hand
device.establishConnection(false)
.flatMapSingle { rxBleConnection ->
Single.zip(
rxBleConnection
.readCharacteristic(jUuid(ManufacturerNameString.toString())),
rxBleConnection
.readCharacteristic(jUuid(ModelNumberString.toString())),
rxBleConnection
.readCharacteristic(jUuid(SerialNumberString.toString())),
rxBleConnection
.readCharacteristic(jUuid(HardwareRevisionString.toString())),
rxBleConnection
.readCharacteristic(jUuid(FirmwareRevisionString.toString())),
rxBleConnection
.readCharacteristic(jUuid(SoftwareRevisionString.toString())),
rxBleConnection
.readCharacteristic(jUuid(SystemID.toString())),
rxBleConnection
.readCharacteristic(jUuid(PnPID.toString())),
Function8 { a, b, c, d, e, f, g, h -> BoseDevice(a, b, c, d, e, f, g, h)}
)
}
.take(1)
.subscribe({}, {})
or
device.establishConnection(false)
.flatMap { rxBleConnection ->
Observable.combineLatest(
rxBleConnection
.readCharacteristic(jUuid(ManufacturerNameString.toString())).toObservable(),
rxBleConnection
.readCharacteristic(jUuid(ModelNumberString.toString())).toObservable(),
rxBleConnection
.readCharacteristic(jUuid(SerialNumberString.toString())).toObservable(),
rxBleConnection
.readCharacteristic(jUuid(HardwareRevisionString.toString())).toObservable(),
rxBleConnection
.readCharacteristic(jUuid(FirmwareRevisionString.toString())).toObservable(),
rxBleConnection
.readCharacteristic(jUuid(SoftwareRevisionString.toString())).toObservable(),
rxBleConnection
.readCharacteristic(jUuid(SystemID.toString())).toObservable(),
rxBleConnection
.readCharacteristic(jUuid(PnPID.toString())).toObservable(),
Function8 { a, b, c, d, e, f, g, h -> BoseDevice(a, b, c, d, e, f, g, h)}
)
}
.take(1)
.subscribe({}, {})

Related

Understanding this Mutable Recursive Function using the uJson library

I am trying to implement an insert function using the ujson library:
Here is my attempt:
import ujson.{Obj, Value}
import upickle.default._
object Example extends App {
def r = transform(
List(Map(
"a" -> Map("b" -> Obj("c" -> List(1,2,3), "d" -> List(2,4,6))),
"e" -> Map("f" -> Obj("g" -> List(1,2,3)))))
).to(Value)
def insert(j: ujson.Value, k: String, v: ujson.Value): Unit = j match {
case a: ujson.Arr => a.arr.foreach(e => insert(e, k, v))
case o: ujson.Obj =>
if (o.obj.keySet contains k) o.obj(k) = v
else o.obj.values.foreach(e => insert(e, k, v))
case _ => Nil
}
println(r)
insert(r, "b", transform(None).to(Value))
println(r)
}
However, this gives me output that is unchanged:
[{"a":{"b":{"c":[1,2,3],"d":[2,4,6]}},"e":{"f":{"g":[1,2,3]}}}]
[{"a":{"b":{"c":[1,2,3],"d":[2,4,6]}},"e":{"f":{"g":[1,2,3]}}}]
Given that the Value type is mutable, why does this not mutate and update the key, k, with value v for json value object r?
You are creating Value anew every time you call r so, every changes you would make to it, are dismissed.
You create one copy when you call println(r).
Then you create a separate copy with insert(r, "b", transform(None).to(Value)), mutate it and dismiss.
Then you are creating third copy with another println(r).
If you want to refer to the same object use val instead of def.

An observable to emit tuple of latest values of N other observables?

Is there any operation like zip, but which not waits for entire tuple gather, but emits tuple on each change.
For example if it just emitter 1A and B came on second observable, it immediately emits 1B, which is "latest" tuple.
At the beginning, this operation should wait until all N elements gather.
What you're looking for is usually called zipLatest.
Here's an example implementation in Python:
from typing import *
import rx
import rx.operators as ops
def zip_latest(*xss: rx.Observable) -> rx.Observable:
helper = ZipLatestHelper(len(xss))
return mux(*xss).pipe(
ops.map(helper.process),
ops.filter(lambda x: x is not None),
)
def mux(*xss: rx.Observable) -> rx.Observable:
def pair_index(i: int) -> Callable[[Any], Tuple[int, Any]]:
def inner(x: Any) -> Tuple[int, Any]:
return i, x
return inner
paired = [xs.pipe(ops.map(pair_index(i))) for i, xs in enumerate(xss)]
return rx.from_iterable(paired).pipe(ops.merge_all())
class ZipLatestHelper:
def __init__(self, num_streams):
self.latest = [None for _ in range(num_streams)]
self.ready = set()
def process(self, pair: Tuple[int, Any]) -> Optional[Tuple[Any, ...]]:
i, x = pair
self.latest[i] = x
self.ready.add(i)
return (
tuple(self.latest) if len(self.ready) == len(self.latest) else None
)
And usage:
from time import sleep
zipped = zip_latest(
rx.interval(0.5).pipe(ops.map(lambda i: f"A{i}")),
rx.interval(0.3).pipe(ops.map(lambda i: f"B{i}")),
)
zipped.subscribe(print)
sleep(10)
With output:
('A0', 'B0')
('A0', 'B1')
('A0', 'B2')
('A1', 'B2')
('A1', 'B3')
('A2', 'B3')
('A2', 'B4')
('A2', 'B5')
Caveats:
May not be thread-safe.
What should one do if a stream receives OnCompleted? Should the zipped stream continue emitting items, or should it issue an OnCompleted?
What should one do if a stream receives OnError?

Topological sort in scala

I'm looking for a nice implementation of topological sorting in scala.
The solution should be stable:
If input is already sorted, the output should be unchanged
The algorithm should be deterministic (hashCode has no effect)
I suspect there are libraries that can do this, but I wouldn't like to add nontrivial dependencies due to this.
Example problem:
case class Node(name: String)(val referenced: Node*)
val a = Node("a")()
val b = Node("b")(a)
val c = Node("c")(a)
val d = Node("d")(b, c)
val e = Node("e")(d)
val f = Node("f")()
assertEquals("Previous order is kept",
Vector(f, a, b, c, d, e),
topoSort(Vector(f, a, b, c, d, e)))
assertEquals(Vector(a, b, c, d, f, e),
topoSort(Vector(d, c, b, f, a, e)))
Here the order is defined such that if the nodes were say declarations in a programming language referencing other declarations, the result order would
be such that no declaration is used before it has been declared.
Here is my own solution. Additionnally it returns possible loops detected in the input.
The format of the nodes is not fixed because the caller provides a visitor that
will take a node and a callback and call the callback for each referenced node.
If the loop reporting is not necessary, it should be easy to remove.
import scala.collection.mutable
// Based on https://en.wikipedia.org/wiki/Topological_sorting?oldformat=true#Depth-first_search
object TopologicalSort {
case class Result[T](result: IndexedSeq[T], loops: IndexedSeq[IndexedSeq[T]])
type Visit[T] = (T) => Unit
// A visitor is a function that takes a node and a callback.
// The visitor calls the callback for each node referenced by the given node.
type Visitor[T] = (T, Visit[T]) => Unit
def topoSort[T <: AnyRef](input: Iterable[T], visitor: Visitor[T]): Result[T] = {
// Buffer, because it is operated in a stack like fashion
val temporarilyMarked = mutable.Buffer[T]()
val permanentlyMarked = mutable.HashSet[T]()
val loopsBuilder = IndexedSeq.newBuilder[IndexedSeq[T]]
val resultBuilder = IndexedSeq.newBuilder[T]
def visit(node: T): Unit = {
if (temporarilyMarked.contains(node)) {
val loopStartIndex = temporarilyMarked.indexOf(node)
val loop = temporarilyMarked.slice(loopStartIndex, temporarilyMarked.size)
.toIndexedSeq
loopsBuilder += loop
} else if (!permanentlyMarked.contains(node)) {
temporarilyMarked += node
visitor(node, visit)
permanentlyMarked += node
temporarilyMarked.remove(temporarilyMarked.size - 1, 1)
resultBuilder += node
}
}
for (i <- input) {
if (!permanentlyMarked.contains(i)) {
visit(i)
}
}
Result(resultBuilder.result(), loopsBuilder.result())
}
}
In the example of the question this would be applied like this:
import TopologicalSort._
def visitor(node: BaseNode, callback: (Node) => Unit): Unit = {
node.referenced.foreach(callback)
}
assertEquals("Previous order is kept",
Vector(f, a, b, c, d, e),
topoSort(Vector(f, a, b, c, d, e), visitor).result)
assertEquals(Vector(a, b, c, d, f, e),
topoSort(Vector(d, c, b, f, a, e), visitor).result)
Some thoughts on complexity:
The worst case complexity of this solution is actually above O(n + m) because the temporarilyMarked array is scanned for each node.
The asymptotic complexity would be improved if the temporarilyMarked would be replaced with for example a HashSet.
A true O(n + m) would be achieved if the marks were be stored directly inside the nodes, but storing them outside makes writing a generic solution easier.
I haven't run any performance tests, but I suspect scanning the temporarilyMarked array is not a problem even in large graphs as long as they are not very deep.
Example code and test on Github
I have very similar code is also published here. That version has a test suite which can be useful for experimenting and exploring the implementation.
Why would you detect loops
Detecting loops can be useful for example in serialization situations where most of the data can be handled as a DAG, but loops can be handled with some kind of special arrangement.
The test suite in the Github code linked to in above section contains various cases with multiple loops.
Here's a purely functional implementation that returns the topological ordering ONLY if the graph is acyclic.
case class Node(label: Int)
case class Graph(adj: Map[Node, Set[Node]]) {
case class DfsState(discovered: Set[Node] = Set(), activeNodes: Set[Node] = Set(), tsOrder: List[Node] = List(),
isCylic: Boolean = false)
def dfs: (List[Node], Boolean) = {
def dfsVisit(currState: DfsState, src: Node): DfsState = {
val newState = currState.copy(discovered = currState.discovered + src, activeNodes = currState.activeNodes + src,
isCylic = currState.isCylic || adj(src).exists(currState.activeNodes))
val finalState = adj(src).filterNot(newState.discovered).foldLeft(newState)(dfsVisit(_, _))
finalState.copy(tsOrder = src :: finalState.tsOrder, activeNodes = finalState.activeNodes - src)
}
val stateAfterSearch = adj.keys.foldLeft(DfsState()) {(state, n) => if (state.discovered(n)) state else dfsVisit(state, n)}
(stateAfterSearch.tsOrder, stateAfterSearch.isCylic)
}
def topologicalSort: Option[List[Node]] = dfs match {
case (topologicalOrder, false) => Some(topologicalOrder)
case _ => None
}
}

How to use Reduce on Scala

I am using scala to implement an algorithm. I have a case where I need to implement such scenario:
test = Map(t -> List((t,2)), B -> List((B,3), (B,1)), D -> List((D,1)))
I need to some the second member of every common tuples.
The desired result :
Map((t,2),(B,4),(D,1))
val resReduce = test.foldLeft(Map.empty[String, List[Map.empty[String, Int]]){(count, tup) => count + (tup -> (count.getOrElse(tup, 0) + 1))
I am trying to use "Reduce", I have to go through every group I did and sum their second member. Any idea how to do that.
If you know that all lists are nonempty and start with the same key (e.g. they were produced by groupBy), then you can just
test.mapValues(_.map(_._2).sum).toMap
Alternatively, you might want an intermediate step that allows you to perform error-checking:
test.map{ case(k,xs) =>
val v = {
if (xs.exists(_._1 != k)) ??? // Handle key-mismatch case
else xs.reduceOption((l,r) => l.copy(_2 = l._2 + r._2))
}
v.getOrElse(??? /* Handle empty-list case */)
}
You could do something like this:
test collect{
case (key, many) => (key, many.map(_._2).sum)
}
wherein you do not have to assume that the list has any members. However, if you want to exclude empty lists, add a guard
case (key, many) if many.nonEmpty =>
like that.
scala> val test = Map("t" -> List(("t",2)), "B" -> List(("B",3), ("B",1)), "D" -> List(("D",1)))
test: scala.collection.immutable.Map[String,List[(String, Int)]] = Map(t -> List((t,2)), B -> List((B,3), (B,1)), D -> List((D,1)))
scala> test.map{case (k,v) => (k, v.map(t => t._2).sum)}
res32: scala.collection.immutable.Map[String,Int] = Map(t -> 2, B -> 4, D -> 1)
Yet another approach, in essence quite similar to what has already been suggested,
implicit class mapAcc(val m: Map[String,List[(String,Int)]]) extends AnyVal {
def mapCount() = for ( (k,v) <- m ) yield { (k,v.map {_._2}.sum) }
}
Then for a given
val test = Map("t" -> List(("t",2)), "B" -> List(("B",3), ("B",1)), "D" -> List(("D",1)))
a call
test.mapCount()
delivers
Map(t -> 2, B -> 4, D -> 1)

Is it possible to have a map of {key -> function call} in Scala?

I'm trying to create a class that has a map of keys -> function calls, and the following code is not behaving as I would like it to.
class MyClass {
val rnd = scala.util.Random
def method1():Double = {
rnd.nextDouble
}
def method2():Double = {
rnd.nextDouble
}
def method3():Double = {
rnd.nextDouble
}
def method4():Double = {
rnd.nextDouble
}
def method5():Double = {
rnd.nextDouble
}
var m = Map[String,Double]()
m += {"key1"-> method1}
m += {"key2"-> method2}
m += {"key3"-> method3}
m += {"key4"-> method4}
m += {"key5"-> method5}
def computeValues(keyList:List[String]):Map[String,Double] = {
var map = Map[String,Double]()
keyList.foreach(factor => {
val value = m(factor)
map += {factor -> value}
})
map
}
}
object Test {
def main(args : Array[String]) {
val b = new MyClass
for(i<-0 until 3) {
val computedValues = b.computeValues(List("key1","key4"))
computedValues.foreach(element => println(element._2))
}
}
}
The following output
0.022303440910331762
0.8557634244639081
0.022303440910331762
0.8557634244639081
0.022303440910331762
0.8557634244639081
indicates that once the function is placed in the map, it's a frozen instance (each key producing the same value for each pass). The behavior I would like to see is that the key would refer to a function call, generating a new random value rather than just returning the instance held in the map.
The problem is with the signature of your map m. You described that you wanted to put functions in the map; however you've declared it as Map[String, Double] which is just a map of strings to doubles. The correct type would be Map[String, () => Double].
Because brackets are optional on no-arg method invocations, the difference in types here is very important. When the map is being populated, the methods are invoked at insertion time in order to match the type signature (I believe this is done by an implicit conversion but I'm not 100% sure).
By simply changing the declared signature of your map, the functions are inserted as you desire, and can be evaluated during computeValues (requires a change on line 35 to map += {factor -> value()}), resulting in the following output (tested on Scala 2.8):
0.662682479130198
0.5106611727782306
0.6939805749938253
0.763581022199048
0.8785861039613938
0.9310533868752249
You need to map the keys to functions, not to the answer that the function would give you. Try this:
var m = Map[String,() => Double]()
m += /* etc. */
m.values.foreach(println(x => x()))
m.values.foreach(println(x => x()))
I would use scala's type inference to define the map.
There's no need to define the methods separately, if they're only used via the map.
Also you can use an immutable val, instead of a mutable var.
val m = Map( "key1" -> {() => rnd.nextDouble},
"key2" -> {() => rnd.nextDouble},
"key3" -> {() => rnd.nextDouble},
"key4" -> {() => rnd.nextDouble},
"key5" -> {() => rnd.nextDouble})
You also need to change line 35 to value()